2015-11-05 11 views
5

Supponiamo quindi di avere una classe, che contenga oggetti funzionali e nella chiamata del costruttore passino gli argomenti, che devono essere passati all'oggetto funzionale un po 'di tempo dopo. Qualcosa di simile:Variadic - come posso creare il tipo, che memorizza gli argomenti passati

class Binder{ 
public: 
    Binder(functional_object, listOfParameters); 
    callFunctionalObject(); // calls functional object with given list of parameters 
}; 

Prima di C++ 11 non ho potuto utilizzare i modelli variadic, così si potrebbe fare:

struct none{}; 

template <typename T1, typename T2=none, typename T3=none> 
class Binder{ 
public: 
    Binder(T1 functionalObject, T2 arg1=none(), T3arg3=none()); 
    void callFunctionalObject(); 
private: 
    T1 m_functionalObject; 
    T2 m_arg1; 
    T3 m_arg2; 
}; 

Dove callFunctionalobject potrebbero essere attuate come segue:

template<typename T1, typename T2, typename T3> 
void Binder<T1,T2,T3>::callFunctionalObject(){ 
    callImpl(m_functionalObject, m_arg1, m_arg2); 
} 

e callImpl verrebbe sovraccaricato per riconoscere oggetti di tipo none per passare una quantità adeguata di argomenti all'oggetto funzionale.

Ora passando a C++ 11 non so come implementare il fatto, che nella sezione privata ho membri, a cui ho un accesso diretto.

Qualcuno potrebbe spiegarmi il modo in cui posso fare lo stesso utilizzando C++ 11 o C++ 14?

risposta

7

È necessario memorizzare un std::function e un std::tuple e quindi chiamare la funzione sulla tupla.

Qui una soluzione di lavoro C++ 14

#include <iostream> 
#include <functional> 

template<typename T1, typename ...T> 
class Binder 
{ 
public: 
    Binder(std::function<T1(T...)> f, std::tuple<T...> t) : m_functional_obj(f), m_parameters(t) {} 

    template<std::size_t ...I> 
    T1 callImpl(std::index_sequence<I...>) 
    { 
     return m_functional_obj(std::get<I>(m_parameters)...); 
    } 

    T1 callFunctionalObject() 
    { 
     return callImpl(std::index_sequence_for<T...>{}); 
    } 
private: 
    std::function<T1(T...)> m_functional_obj; 
    std::tuple<T...>  m_parameters; 
}; 

int test(int i) 
{ 
    std::cout << "test(" << i << ")" << std::endl;  
    return i + 1; 
} 

int main() 
{ 
    Binder<int,int> bibi(test, std::make_tuple<int>(2)); 
    auto res = bibi.callFunctionalObject(); 
    std::cout << "res is " << res << std::endl; 
} 

Live code

+2

ho pensato a qualcosa di simile, ma non sarebbe functionalObject quindi ottenere una touple nei parametri invece di lista normale dei parametri? – DawidPi

+2

Unpacking tuple parameters ha bisogno di un po 'di lavoro per cercare di ottenere una soluzione di codice live per te :) – coincoin

+1

@DawidPi Aggiornamento di una soluzione C++ 14 usando 'std :: index_sequence' – coincoin

2

Il modo più semplice è quello di memorizzare un oggetto std::function con argomenti già impostati utilizzando std::bind:

class Binder{ 
public: 
    template <typename T1, typename... T2> 
    Binder(T1 functionalObject, T2... args) : f(std::bind(functionalObject, args...)) {} 
    void callFunctionalObject() { f(); } 
private: 
    std::function<void()> f; 
}; 

void foo(int n, std::string s) { 
    std::cout << n << " " << s << std::endl; 
} 

int main() 
{ 
    Binder b(foo, 42, "test"); 
    b.callFunctionalObject(); 
} 

Se hai bisogno di qualcosa di più avanzato, potresti voler memorizzare la funzione argu in e std::tuple e quindi utilizzare alcuni modelli di magia per scartarlo, ma si prega di specificare che cosa esattamente avete bisogno nella domanda.

P.S. Vedi anche "unpacking" a tuple to call a matching function pointer

+0

Ok std :: bind sembra ok e facile, ma ancora non so che tipo di meccanismo si stia nascondendo. Dovrei provare a cercare l'implementazione di std :: bind. Quello che sto cercando è la stessa funzionalità fornita dal codice in questione ma più flessibile (il numero massimo di parametri non è impostato dall'inizio) utilizzando i modelli Variadic. – DawidPi

+1

Non penso che siano necessari i parametri del template a livello di classe, potreste semplicemente creare un modello con il costruttore e le persone potrebbero semplicemente usare 'Binder b (foo, 32," test ");'. – SirGuy

+0

@GuyGreer, infatti, aggiornato – Petr

4

Il mio esempio:

// Indices 
template <std::size_t... Is> 
struct Indices {}; 

template <std::size_t N, std::size_t... Is> 
struct BuildIndices : BuildIndices <N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct BuildIndices<0, Is...> : Indices <Is...> {}; 

template<class FuncObject, class ... T> 
class Binder 
{ 
public: 
    Binder(FuncObject funcObject, T... args) 
     : m_funcObject(funcObject), m_arguments(std::make_tuple(args...)) 
    { 
    } 

    void Call() 
    { 
     DoCall(BuildIndices<sizeof ... (T)> {}); 
    } 

private: 
    template<size_t... Ind> 
    void DoCall(Indices<Ind...>) 
    { 
     return m_funcObject(std::get<Ind>(m_arguments)...); 
    } 

    FuncObject m_funcObject; 
    std::tuple<T...> m_arguments; 
}; 

void Foo(int, char) 
{ 
} 

int main() 
{ 
    Binder<void(*)(int, char), int, char> f(Foo, 1, 'd'); 
    f.Call(); 

    return 0; 
} 
+1

Questo è buono, in più in C++ 14 non hai più bisogno delle tue classi 'Indices' e' BuildIndices'. – SirGuy

Problemi correlati