Erstens haben Sie im Hinterkopf, dass Sie nicht direkt tun können, was Sie wollen. C++ ist eine stark typisierte Sprache, daher muss der Typ des Funktionsergebnisses bei Kompilierungszeit bekannt sein. Wenn also die Zeichenfolge, die Sie an den Getter übergeben, zur Laufzeit bekannt ist, können Sie die Funktion nicht zur Kompilierzeit senden, damit der Compiler den entsprechenden Ergebnistyp ableiten kann. Aber wenn Sie akzeptieren, dass Sie Typ-löschen müssen, um den Getter-Ergebnistyp zu löschen, könnten Sie z. boost::variant
um mit Ihrem Problem umzugehen. C++ 14 Beispiel (unter Verwendung von Boost, da C++ 17-Variante sollte in std verfügbar):
#include <boost/variant.hpp>
#include <utility>
#include <iostream>
#include <tuple>
template< typename T >
struct name_value_pair
{
using type = T;
std::string name;
T value;
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct getter;
template <std::size_t N, std::size_t... Is>
struct getter<N, std::index_sequence<Is...>> {
template <class Val, class Res>
void setRes(Val &val, Res &res, std::string &s) {
if (val.name == s)
res = val.value;
}
template <class Tup>
auto operator()(Tup &tuple_vals, std::string &s) {
boost::variant<typename std::tuple_element<Is, Tup>::type::type...> result;
int helper[] = { (setRes(std::get<Is>(tuple_vals), result, s), 1)... };
(void)helper;
return result;
}
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct setter;
template <std::size_t N, std::size_t... Is>
struct setter<N, std::index_sequence<Is...>> {
template <class Val, class SVal>
std::enable_if_t<!std::is_same<SVal, typename Val::type>::value> setVal(Val &, std::string &, const SVal &) { }
template <class Val>
void setVal(Val &val, std::string &s, const typename Val::type &sval) {
if (val.name == s)
val.value = sval;
}
template <class Tup, class Val>
auto operator()(Tup &tuple_vals, std::string &s, const Val &val) {
int helper[] = { (setVal(std::get<Is>(tuple_vals), s, val), 1)... };
(void)helper;
}
};
template <class T, class Res>
using typer = Res;
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
auto get_value(std::string n)
{
return getter<sizeof...(Ts)>{}(_name_value_pairs, n);
}
template <class T>
void set_value(std::string n, const T& value) {
setter<sizeof...(Ts)>{}(_name_value_pairs, n , value);
}
void set_names(typer<Ts, std::string>... names) {
_name_value_pairs = std::make_tuple(name_value_pair<Ts>{names, Ts{}}...);
}
private:
std::tuple<name_value_pair<Ts>...> _name_value_pairs;
};
int main() {
tuple_of_name_value_pairs<int, float, double> t;
t.set_names("abc", "def", "ghi");
t.set_value("abc", 1);
t.set_value("def", 4.5f);
t.set_value("ghi", 5.0);
std::cout << t.get_value("def") << std::endl;
}
[live demo]
Ich bin sicher, dass Sie in der Lage sein, den Code zu optimieren (zB machen Verwendung von move semantics/perfect forwarding, etc.). Dies soll Ihnen nur zeigen, wie Sie Ihre Implementierung starten können.
Können Sie die gewünschte Verwendung Ihrer 'get_value'-Funktion anzeigen? Insbesondere werden Zeichenfolgenschlüssel zur Kompilierzeit bekannt sein oder werden nicht bekannt sein, bis Programm läuft? –
Die Zeichenfolge wird nicht bekannt sein, bis das Programm ausgeführt wird. Die Funktion "get" wird Teil der C++ - Bibliotheks-API sein. Die Namen werden als "Zeichenfolgen" übergeben, da dies für den Programmierer bequemer ist als die Verwendung von Indizes. –
Was wäre die Problemumgehung, wenn der String zur Kompilierzeit bekannt ist? –