2015-11-05 3 views
5

Angenommen, ich habe eine Klasse, die ein funktionales Objekt enthält, und im Konstruktoraufruf übergeben ich Argumente, die irgendwann an das funktionale Objekt übergeben werden sollen später. Etwas wie:Variadische Vorlagen - wie kann ich einen Typ erstellen, der übergebene Argumente speichert

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

Bevor C 11 ++ ich nicht Variadische Vorlagen verwenden könnte, so würde man tun:

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

Wo callFunctionalobject umgesetzt werden könnten wie folgt:

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

und callImpl würde überladen werden, um Objekte des Typs none zu erkennen, um die richtige Anzahl von Argumenten an das funktionale Objekt zu übergeben.

Nun zu C++ 11 wechseln Ich weiß nicht, wie ich die Tatsache implementieren soll, dass ich im privaten Bereich Mitglieder habe, auf die ich direkten Zugriff habe.

Kann mir jemand erklären, wie ich dasselbe mit C++ 11 oder C++ 14 machen kann?

Antwort

7

Sie sollten eine std::function und eine std::tuple speichern und dann die Funktion auf dem Tupel aufrufen.

hier ein Arbeits C++ 14-Lösung

#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

Ich dachte an so etwas, aber ich würde nicht functionalObject dann einen touple in den Parametern anstelle der normalen Liste der Parameter erhalten? – DawidPi

+2

Das Auspacken von Tupel-Parametern erfordert ein wenig Arbeit, um eine Live-Code-Lösung für Sie zu bekommen :) – coincoin

+1

@DawidPi Eine C++ 14-Lösung mit 'std :: index_sequence' aktualisiert – coincoin

2

Der einfachste Weg, um ein Objekt mit std::function bereits eingestellten Argumente zu speichern ist std::bind verwendet:

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

Wenn Sie etwas fortgeschritteneres brauchen, dann möchten Sie vielleicht die Funktion argue speichern in und std::tuple und dann verwenden Sie einige Vorlage Magie, um es auszupacken, aber bitte genau angeben, was Sie in der Frage brauchen.

P.S. Siehe auch "unpacking" a tuple to call a matching function pointer

+0

Ok std :: bind scheint in Ordnung und einfacher Weg, aber ich weiß immer noch nicht, welche Art von Mechanismus dahinter steckt. Ich sollte dann versuchen, nach std :: bind Implementierung zu suchen. Was ich suche ist die gleiche Funktionalität wie Code geben n in Frage, aber flexibler (maximale Anzahl der Parameter ist nicht von Anfang an eingerichtet) mit Variadic Templates. – DawidPi

+1

Ich glaube nicht, dass Sie die Template-Parameter auf Klassenebene benötigen, Sie könnten einfach den Konstruktor zu einer Vorlage machen und die Leute könnten einfach 'Binder b (foo, 32," test ");'. – SirGuy

+0

@GuyGreer, in der Tat, aktualisiert – Petr

4

Mein Beispiel:

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

Das ist gut, plus in C++ 14 brauchst du keine eigenen 'Indices' und' BuildIndices' Klassen mehr. – SirGuy

Verwandte Themen