2016-03-09 6 views
2

Я реализую функцию вариационного шаблона, потому что я хочу совершать вызовы с 7 параметрами. Мои звонки идут так.Как извлечь параметры из пакета параметров функции шаблона?

foo(1, 2, "msg", 4, 5.0); 

или

foo(3, 4.1, "msg"); 

Первый параметр определяет используемый протокол, и каждый параметр после этого является то, что я хотел бы поместить в STRUCT Msg.

struct Msg { 
    int proto; 
    string str; 
    int a; 
    int b; 
    double d; 
}; 

Самая большая проблема, с которой я сталкиваюсь, - я не знаю, как получить оставшиеся параметры после первого и сохранить их. Я хотел бы использовать первый параметр, чтобы указать, какие элементы структуры должны быть заполнены. Часть, которая меня смущает, состоит в том, что каждый рекурсивный вызов меняет функцию sig.

template<typename T> 
T bar(T t) { 
    cout << __PRETTY_FUNCTION__ << endl; 
    return t; 
} 

template<typename T, typename... Args> 
void foo(T value, Args... args) 
{ 
    cout << __PRETTY_FUNCTION__ << endl; 
    Msg msg; 
    msg.proto = value; 

    switch (value) { 
    case PROTO_A: 
     // when calling 'foo(1, 2, "msg", 4, 5.0)' 
     // 1 is proto and placed in struct Msg (msg.proto = value) 
     // but how to get the remaining params from args into struct Msg 
     foo(args...); 
     break; 

    case PROTO_B: 
     foo(args...); 
     break; 

    default: 
     break; 
    } 

    send_msg(msg); 
} 
+2

Похоже, проблема XY, хотя четко обозначены как X, так и Y. –

ответ

1

Вы можете использовать его без рекурсии:

Вашего конкретного метода заполнения, с резервным ипом вызова (с использованием SFINAE и приоритета):

struct overload_priority_low {}; 
struct overload_priority_high : overload_priority_low {}; 

template<typename... Args> 
auto Fill_ProtoA(Msg& msg, overload_priority_high, Args... args) 
-> decltype(std::tie(msg.a, msg.str, msg.b, msg.d) = std::tie(args...), void()) 
{ 
    std::tie(msg.a, msg.str, msg.b, msg.d) = std::tie(args...); 
} 

template<typename... Args> auto Fill_ProtoA(Msg& msg, overload_priority_low, Args... args) 
{ 
    throw std::runtime_error("Should not be called"); 
} 


template<typename... Args> 
auto Fill_ProtoB(Msg& msg, overload_priority_high, Args&&...args) 
-> decltype(std::tie(msg.d, msg.str) = std::tie(args...), void()) 
{ 
    std::tie(msg.d, msg.str) = std::tie(args...); 
    msg.a = 42; 
    msg.b = 42; 
} 

template<typename... Args> auto Fill_ProtoB(Msg& msg, overload_priority_low, Args... args) 
{ 
    throw std::runtime_error("Should not be called"); 
} 

А потом ваш диспетчерский foo:

template<typename T, typename... Args> 
void foo(T value, Args... args) 
{ 
    std::cout << __PRETTY_FUNCTION__ << std::endl; 
    Msg msg; 
    msg.proto = value; 

    switch (value) { 
     case PROTO_A: Fill_ProtoA(msg, overload_priority_high{}, args...); break; 
     case PROTO_B: Fill_ProtoB(msg, overload_priority_high{}, args...); break; 
     default: break; 
    } 
    send_msg(msg); 
} 

Demo

0

Если я правильно понял вашу проблему, вы хотите построить VARIADIC шаблон функции, для которых:

  • первым параметром является номер протокола
  • Следующие параметры могут быть различных типов и ALIMENT на структуру

Так что я предполагаю, что вы уже определили один struct Msg (в зависимости от T?) и что вы можете написать функцию, которая Aliments на-структуру с одной парам с использованием прото из структуры:

template <typename Arg> 
void process(struct Msg& msg, Arg value) { 
    switch(msg.proto) { 
    case PROTO_A: 
     ... 
    } 
} 

(возможно process(struct Msg<T>& msg, ...), но я оставлю это для вас, потому что вы не сказали, как обрабатывать отдельные параметры ...

Вы можете написать рекурсивную версию процесса:

template <typename First first, typename... Args> 
void process(struct Msg& msg, First first, Args ... args) { 
    process(msg, first); 
    if (sizeof...(args) > 0) { 
     process(msg, args...); 
    } 
} 

И ваша функция foo может стать:

template<typename T, typename... Args> 
void foo(T value, Args... args) 
{ 
    cout << __PRETTY_FUNCTION__ << endl; 
    Msg msg; 
    msg.proto = value; 

    process(msg, args); 

    send_msg(msg); 
} 
0

Это обобщение, которое может вам пригодиться. Здесь переданные аргументы могут быть в любом порядке (даже пустом). Компилятор выведет, какие аргументы будут назначены тем членам данных рассматриваемого объекта (если вообще).

#include <iostream> 
#include <tuple> 
#include <string> 

constexpr std::size_t NOT_FOUND = -1; 
using default_tuple = std::tuple<int, bool, double, char, std::string>; 
template <typename, std::size_t> struct Pair; 

template <typename Tuple, typename T, std::size_t Start, typename = void> 
struct Find : std::integral_constant<std::size_t, NOT_FOUND> {}; 

template <typename Tuple, typename T, std::size_t Start> 
struct Find<Tuple, T, Start, std::enable_if_t<(Start < std::tuple_size<Tuple>::value)>> { 
    static constexpr size_t value = std::is_same<std::remove_reference_t<T>, std::tuple_element_t<Start, Tuple>>::value ? Start : Find<Tuple, T, Start+1>::value; 
}; 

template <typename T, typename... Pairs> struct SearchPairs; 

template <typename T> 
struct SearchPairs<T> : std::integral_constant<std::size_t, 0> {}; 

template <typename T, typename First, typename... Rest> 
struct SearchPairs<T, First, Rest...> : SearchPairs<T, Rest...> {}; 

template <typename T, std::size_t I, typename... Rest> 
struct SearchPairs<T, Pair<T,I>, Rest...> : std::integral_constant<std::size_t, I> {}; 

template <typename Tuple, typename ArgsTuple, std::size_t Start, std::size_t OriginalSize, typename Indices, typename LastIndices, typename = void> 
struct ObtainIndices { 
    using type = Indices; 
}; 

template <typename Tuple1, typename Tuple2, std::size_t Start, std::size_t OriginalSize, std::size_t... Is, typename... Pairs> 
struct ObtainIndices<Tuple1, Tuple2, Start, OriginalSize, std::index_sequence<Is...>, std::tuple<Pairs...>, 
     std::enable_if_t<(Start < std::tuple_size<Tuple2>::value)>> { 
    using T = std::tuple_element_t<Start, Tuple2>; 
    static constexpr std::size_t start = SearchPairs<T, Pairs...>::value, // Searching through Pairs..., and will be 0 only if T is not found among the pairs. Else we start after where the last T was found in Tuple1. 
     index = Find<Tuple1, T, start>::value; 
    using type = std::conditional_t<(index < OriginalSize), 
     typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index+1>, Pairs...>>::type, // Pair<T, index+1> because we start searching for T again (if ever) after position 'index'. Also, we must place Pair<T, index+1> before the Pairs... pack rather than after it because if a Pair with T already exists, that Pair must not be used again. 
     typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index>, Pairs...>>::type // We add Pair<T, index> right before Pairs... since we want to use the default T value again if another T value is ever needed. Now this could clutter up the std::tuple<Pairs...> pack with many Pairs, causing more compile time, but there is no guarantee that Pair<T, index+1> is already there (since this default T value could be the first T value found). 
    >; 
}; 

template <std::size_t I, std::size_t J, typename Tuple1, typename Tuple2> 
void assignHelper (Tuple1&& tuple1, const Tuple2& tuple2) { 
    if (std::get<J>(tuple2) != std::tuple_element_t<J, Tuple2>()) // Make the assignment only if the right hand side is not the default value. 
     std::get<I>(std::forward<Tuple1>(tuple1)) = std::get<J>(tuple2); 
} 

template <typename Tuple1, typename Tuple2, std::size_t... Is, std::size_t... Js> 
void assign (Tuple1&& tuple1, Tuple2&& tuple2, std::index_sequence<Is...>, std::index_sequence<Js...>) { 
    const int a[] = {(assignHelper<Is, Js>(tuple1, tuple2), 0)...}; 
    static_cast<void>(a); 
} 

template <typename T, typename... Args> 
void fillData (T& t, Args&&... args) { 
    auto s = t.tuple_ref(); 
    std::tuple<Args...> a = std::tie(args...); 
    const auto tuple = std::tuple_cat(a, default_tuple{}); // Add default values for each type, in case they are needed if those types are absent in 'a'. 
    using IndexSequence = typename ObtainIndices<std::remove_const_t<decltype(tuple)>, decltype(s), 0, sizeof...(Args), std::index_sequence<>, std::tuple<>>::type; 
    assign (s, tuple, std::make_index_sequence<std::tuple_size<decltype(s)>::value>{}, IndexSequence{}); 
} 

// Testing 
class Thing { 
    int a, b; 
    char c; 
    double d; 
    std::string s; 
    int n; 
public: 
    auto tuple_ref() {return std::tie(a, b, c, d, s, n);} // This (non-const) member function must be defined for any class that wants to be used in the 'fillData' function. Here 'auto' is std::tuple<int&, int&, char&, double&, std::string&, int&>. 
    void print() const {std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << ", s = " << s << ", n = " << n << '\n';} 
}; 

int main() { 
    Thing thing; 
    fillData (thing, 5, 12.8); 
    thing.print(); // a = 5, b = uninitialized, c = uninitialized, d = 12.8, s = uninitialized, n = uninitialized 
    fillData (thing, 3.14, 2, 'p', std::string("hi"), 5, 'k', 5.8, 10, std::string("bye"), 9); // Note that std::string("hi") must be use instead of "hi" because "hi" is of type const char[2], not std::string (then the program would not compile). See my thread http://stackoverflow.com/questions/36223914/stdstring-type-lost-in-tuple/36224017#36224006 
    thing.print(); // a = 2, b = 5, c = p, d = 3.14, s = hi, n = 10 
    fillData (thing, 4.8, 8, 'q', std::string("hello")); 
    thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = hello, n = 10 
    fillData (thing, std::string("game over")); 
    thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = game over, n = 10 
} 
Смежные вопросы