2013-05-10 9 views
10

Si supponga che non v'è una funzione che accetta più stringhe:Conversione Template Pack variadic in std :: initializer_list

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    // do something 
} 

Ora, ho una funzione variadic template dicono foo() come:

template<typename ...Args> 
void foo() { 
    fun(???); 
} 

Questo il metodo è chiamato esternamente come:

foo<A, B, C, D>(); // where A, B, C, D are classes 

E questi classe s che sono passati come argomenti dovrebbero contenere un comune static const membro:

static const std::string value = "..."; 

Ecco le mie domande (istruzioni):

  1. Quando all'interno foo(), controllare se tutte le Args contengono value utilizzando static_assert
  2. Passare tutti questi valori a fun() per formare un initializer_list; per esempio. fun({A::value, B::value, ...});

Cercato diversi thread relativi ai modelli variadic e la sua estrazione, ma sono ancora novizio in questo settore. La spiegazione in piccoli dettagli è molto apprezzata.

risposta

7

Per quanto riguarda la seconda domanda, basta fare in questo modo:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

Il meccanismo è abbastanza intuitivo: si crea un lista initalizer che contiene lo schema espanso Args::value, risolvendo quindi (nel tuo caso) a { A::value, B::value, C::value, D::value }.

Qui è un programma completo:

#include <string> 
#include <iostream> 

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    { 
     std::cout << s << " "; 
    } 
} 

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

struct A { static std::string value; }; 
struct B { static std::string value; }; 
struct C { static std::string value; }; 
struct D { static std::string value; }; 

std::string A::value = "Hello"; 
std::string B::value = "World"; 
std::string C::value = "of"; 
std::string D::value = "Variadic Templates"; 

int main() 
{ 
    foo<A, B, C, D>(); // where A, B, C, D are classes 
} 

Ed ecco un live example.

quanto riguarda l'affermazione statico, è possibile scrivere un tipo caratteristica che determina se un certo tipo ha una variabile membro value:

template<typename T, typename V = bool> 
struct has_value : std::false_type { }; 

template<typename T> 
struct has_value<T, 
    typename std::enable_if< 
     !std::is_same<decltype(std::declval<T>().value), void>::value, 
     bool 
     >::type 
    > : std::true_type 
{ 
    typedef decltype(std::declval<T>().value) type; 
}; 

Quindi, si potrebbe usare in questo modo:

template<typename T> 
struct check_has_value 
{ 
    static_assert(has_value<T>::value, "!"); 
}; 

template<typename ...Args> 
void foo() { 
    auto l = { (check_has_value<Args>(), 0)... }; 
    fun({Args::value...}); 
} 

Questo è un live example di un controllo riuscito (tutte le classi hanno un membro dati value). Ecco una live example di un controllo di successo ('s classe D membro di dati è chiamato values)

3

La seconda parte è più facile:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

La prima parte è difficile, perché static_assert è una dichiarazione, non un'espressione, in modo che avrebbe dovuto ampliare il pacco variadic entro il primo parametro. Potrebbe essere più facile lasciare che la chiamata a fun esegua il controllo per te. Ecco un abbozzo di come farlo con un ausiliario allconstexpr funzione:

constexpr bool all() { return true; } 
template<typename... Args> constexpr bool all(bool first, Args&&... rest) { 
    return first && all(rest...); 
} 

template<typename ...Args> 
void foo() { 
    static_assert(all(std::is_convertible<decltype(Args::value), 
     std::string>::value...), "All Args must have a value"); 
    fun({Args::value...}); 
} 
+0

Oh Così facile .. hai ragione su 'fun()' facendo la parte; volevo solo salvarmi dagli errori del compilatore. :) – iammilind

+0

Come si verifica se tutti gli argomenti di "Args ..." hanno un membro 'value'? – 0x499602D2

+0

@ 0x499602D2 nota l'espansione del pacchetto all'interno della chiamata a 'all()'. – ecatmur

1

Ecco una risposta a entrambi i punti:

#include <initializer_list> 
#include <iostream> 
#include <string> 
#include <type_traits> 

using namespace std; 

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    cout << s << endl; 
} 

// This uses SFINAE to find if there's a string T::value in T 
template <typename T> 
struct HasValue 
{ 
    typedef char OK; //sizeof() guaranteed 1 
    struct BAD { char x[2]; }; //sizeof() guaranteed >1 

    template <const string *> 
    struct Helper; 

    template <typename X> 
    static OK has(X*, Helper<&X::value>* = nullptr); //SF if &X::value is not a const string* 

    static BAD has(...); //will be picked in SF case 

    static const bool value = (sizeof(has((T*)nullptr)) == sizeof(OK)); 
}; 


// This template (and its specialisation) ensure all args have ::value 
template <typename H, typename... T> 
struct HaveValue : public integral_constant<bool, HasValue<H>::value && HaveValue<T...>::value> 
{}; 

template <typename H> 
struct HaveValue<H> : public HasValue<H> 
{}; 



template <typename... Args> 
void foo() { 
    static_assert(HaveValue<Args...>::value, "All arguments must have const string ::value"); 
    fun({Args::value...}); //answer to point 2: create the initialiser list 
} 

// Example data follow 
struct A 
{ 
    static const string value; 
}; 
const string A::value = "AA"; 

struct B 
{ 
    static const string value; 
}; 
const string B::value = "BB"; 

struct C{}; 

int main() 
{ 
    foo<A, B>(); 
    //foo<A, B, C>(); //uncomment to have the static assertion fire 
} 

See it live.