2016-12-17 6 views
0

ich versuche, eine Datenstruktur zu implementieren, die mehr Namen-Wert-Paar umfasst, wo Werte in ihrer Art unterscheiden:Get-Funktion mit generischem Rückgabetyp

template< typename T > 
struct name_value_pair 
{ 
    std::string name; 
    T   value; 
}; 


template< typename... Ts > 
class tuple_of_name_value_pairs 
{ 
    public: 
    /* type of value */ get_value(std::string n) 
    { 
     // return the value that the element in 
     // _name_value_pairs with name "n" comprises 
    } 

    private: 
    std::tuple<Ts...> _name_value_pairs: 
}; 

Leider habe ich keine Ahnung, wie die get Funktion implementieren .

würde Eine Abhilfe zu Staat Namen wie integer statt string s s und eine Implementierung verwendet nach std::get aber keine Option, hier: der Eingabetyp get eine Zeichenfolge sein muss.

Hat jemand eine Idee?

+0

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? –

+0

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. –

+0

Was wäre die Problemumgehung, wenn der String zur Kompilierzeit bekannt ist? –

Antwort

2

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.

+0

Derzeit, da die Arten von Werten fundamental sind (zB "integer" oder "float"), ist das ' get 'function gibt eine' string'-Darstellung des Wertes zurück (ich denke, 'string' ist den meisten C++ - Programmierern vertrauter als Typen wie' boost :: variant'). Halten Sie dies für eine akzeptable Lösung? –

+1

@abraham_hilbert Nun, es hängt wirklich von Ihrem Anwendungsfall ab. Die Antwort, die ich gebe, ist ziemlich allgemein und erinnert sich tatsächlich an den Typ Ihres Ergebnisses, obwohl es laufzeitabhängig ist ... "std :: string" wird den Typ Ihres Ergebnisses definitiv "vergessen", aber vielleicht in einigen Fällen ist ausreichend... –

Verwandte Themen