2015-11-05 9 views
5

Załóżmy, że mam klasę, która zawiera obiekt funkcjonalny, aw wywołaniu konstruktora przekazuję argumenty, które mają zostać przekazane do obiektu funkcjonalnego w pewnym momencie później. Coś jak:Szablony varariadyczne - jak utworzyć typ, który przechowuje przekazane argumenty?

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

Przed C++ 11 nie mogłem korzystać z szablonów o zmiennej liczbie argumentów, więc należałoby zrobić:

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

Gdzie callFunctionalobject mogą być realizowane w następujący sposób:

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

i callImpl byłby przeciążony, aby rozpoznać obiekty typu none, aby przekazać odpowiednią liczbę argumentów do obiektu funkcjonalnego.

Przejście na C++ 11 Nie wiem, jak zrealizować fakt, że w sekcji prywatnej mam członków, do których mam bezpośredni dostęp.

Czy ktoś może mi wyjaśnić, w jaki sposób mogę zrobić to samo, używając C++ 11 lub C++ 14?

Odpowiedz

7

Powinieneś przechowywać std::function i std::tuple, a następnie wywołać funkcję na krotce.

Tutaj roztwór roboczy 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

myślałem o czymś takim, ale nie functionalObject następnie dostać jeden touple w parametrach zamiast normalnego listy parametrów? – DawidPi

+2

Rozpakowywanie parametrów krotki wymaga trochę pracy, próbując uzyskać rozwiązanie kodu na żywo dla ciebie :) – coincoin

+1

@DawidPi Zaktualizowano rozwiązanie C++ 14 za pomocą 'std :: index_sequence' – coincoin

2

Najprostszym sposobem jest do przechowywania std::function obiekt z już ustalonych argumentów używając 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(); 
} 

Jeśli potrzebujesz czegoś bardziej zaawansowanego, możesz chcieć zapisać argument funkcji w i std::tuple, a następnie użyj magii szablonów, aby ją rozwinąć, ale proszę podać, czego dokładnie potrzebujesz w pytaniu.

P.S. Zobacz także "unpacking" a tuple to call a matching function pointer

+0

OK std :: bind wydaje się być ok i łatwy sposób, ale wciąż nie wiem, jaki mechanizm za tym stoi. Powinienem wtedy spróbować szukać implementacji std :: bind. To, czego szukam, to ta sama funkcjonalność, co kod podający n, ale bardziej elastyczny (maksymalna liczba parametrów nie jest ustawiona od początku) przy użyciu Variadic Templates. – DawidPi

+1

Nie sądzę, że potrzebujesz parametrów szablonu na poziomie klasy, możesz po prostu ustawić konstruktor jako szablon, a ludzie mogą po prostu użyć 'Binder b (foo, 32," test ");'. – SirGuy

+0

@GuyGreer, rzeczywiście, zaktualizowany – Petr

4

Mój przykład:

// 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

To jest dobre, a ponadto w C++ 14 nie potrzebujesz już własnych klas 'Indeksy' i' BuildIndices'. – SirGuy

Powiązane problemy