2016-12-17 4 views
0

я пытаюсь реализовать структуру данных, которая содержит несколько пар имя-значение, где значения могут отличаться от их типа:Получить функцию с родовой тип возвращаемого

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: 
}; 

К сожалению, я не имею ни малейшего представления о том, как реализовать функцию get ,

Обходной бы государственные имена, как integer с вместо string с и использовать реализацию в соответствии с std::get, но это не вариант здесь: тип вход get не должен быть строкой.

У кого-нибудь есть идея?

+0

Не могли бы вы показать желаемое использование функции 'get_value'? В частности, ключи ключей будут известны во время компиляции или не будут известны до запуска программы? –

+0

Строка не будет известна до запуска программы. Функция 'get' будет частью API библиотеки C++. Имена передаются как 'string', так как это будет более удобным для программиста, чем использование индексов. –

+0

Каким будет обходной путь, когда строка известна во время компиляции? –

ответ

2

Прежде всего имейте в виду, что вы не можете делать то, что хотите напрямую. C++ - строго типизированный язык, поэтому тип результата функции должен быть известен в время компиляции. Поэтому, конечно, если строка, которую вы передаете получателю, известна во время выполнения, вы не можете отправлять функцию во время компиляции, чтобы компилятор выводил соответствующий тип результата. Но когда вы соглашаетесь с тем, что вам нужно стирать стирание, чтобы стереть тип результата получения, вы могли бы использовать, например, boost::variant, чтобы решить вашу проблему. C++ 14 Примера (используя импульс, так как C++ 17 варианта должна быть доступны в станде):

#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]

Я уверен, что вы будете в состоянии оптимизировать код (например, сделать использование семантики перемещения/безупречной переадресации и т. д.). Это только для того, чтобы представить вам, как начать свою реализацию.

+0

В настоящее время, поскольку типы значений являются фундаментальными (например, 'integer' или' float'), ' Функция get' равномерно возвращает представление строки в виде строки. Я думаю, что 'string' более знакомы большинству программистов на C++, чем типы, такие как' boost :: variant'. Считаете ли вы, что это приемлемое решение? –

+1

@abraham_hilbert Ну, это действительно зависит от вашего варианта использования. Ответ, который я дал, довольно общий, и на самом деле он «запоминает» тип вашего результата, хотя он зависит от времени выполнения ... 'std :: string' определенно« забудет »тип вашего результата, но, возможно, в некоторых случаях он достаточно... –

Смежные вопросы