2017-02-09 1 views
0

Betrachten Sie das folgende, minimal Beispiel:Typ Löschung und eine Art Template-Methode Muster

struct S { 
    using func_t = void(*)(void *); 

    template<typename T> 
    static void proto(void *ptr) { 
     static_cast<T*>(ptr)->f(); 
    } 

    func_t func; 
    void *ptr; 
}; 

struct T { 
    void f() {} 
}; 

void g(S &s) { 
    s.func(s.ptr); 
} 

int main() { 
    T t; 
    S s; 
    s.func = &S::proto<T>; 
    s.ptr = &t; 
    g(s); 
} 

Die ziemlich offensichtlich Idee ist es, die Art eines Bündels von Objekten zu löschen (wie T, das ist nicht die einzige type), um ein Array von Instanzen von S zu erstellen, dann über dieses Array zu iterieren und eine vorbestimmte Elementfunktion aufzurufen.

So weit so gut, es ist einfach zu implementieren und es funktioniert.
Nun würde Ich mag eine externe Funktion zur Verfügung zu stellen auf dem gelöschten Objekt aufgerufen werden, etwas, das so sein würde:

template<typename T, typename F> 
static void proto(void *ptr, F &&f) { 
    auto *t = static_cast<T*>(ptr); 
    std::forward<F>(f)(*t); 
    t->f(); 
} 

Oder diese:

template<typename T> 
static void proto(void *ptr, void(*f)(T &)) { 
    auto *t = static_cast<T*>(ptr); 
    f(*t); 
    t->f(); 
} 

als aufgerufen werden:

Eine Art Mustervorlagenmuster, bei dem die zusätzlichen Funktionen vom Aufrufer anstelle einer abgeleiteten Klasse bereitgestellt werden.
Leider kann ich das nicht tun, weil ich die Spezialisierungen nicht auf etwas Homogenes reduzieren kann, um einem Funktionszeiger zugewiesen zu werden.

Die einzige Alternative, die ich sehen kann, ist eine benutzerdefinierte Klasse wie die folgenden zu definieren:

struct C { 
    template<typename T> 
    void f(T &t) { /* ... */ } 

    // ... 
}; 

Wo f Versendungen irgendwie den Anruf intern auf die rechte Seite Funktion, dann verwenden Sie es als:

struct S { 
    using func_t = void(*)(void *, C &); 

    template<typename T> 
    static void proto(void *ptr, C &c) { 
     auto t = static_cast<T*>(ptr); 
     c.f(*t); 
     t->f(); 
    } 

    func_t func; 
    void *ptr; 
}; 

Das ist nicht weit von dem, was ich mit einem Lambda tun würde, aber es ist ausführlicher und erfordert, dass ich explizit die Klasse C deklariere.

Gibt es noch eine andere gültige Alternative, um dasselbe zu erreichen, oder ist dies die einzige praktikable Lösung?

+0

Ich fühle ein [XY-Problem] (https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Kannst du uns den Code geben, den du schreiben würdest, wenn du davon ausgibst, dass alle unterstützenden Template-Zauber perfekt funktionieren? – nwp

+0

@nwp Ich gab etwas ähnliches. Stellen Sie sich vor, Sie hätten (würde ich sagen) ein gelöschtes Array, ich würde gerne darüber iterieren, indem ich 's.func (s.ptr, [] (auto obj) {/ * ... * /}); Zeit mit etwas anderen Lambdas. Kann ich Ihnen mehr Details geben? Ich bin mir nicht sicher, wie ich es besser erklären soll. Es tut mir Leid. – skypjack

+0

Was ist das Gemeinsame aller Elemente im Array? Haben sie nur eine gemeinsame Mitgliedsfunktion 'void f()'? – nwp

Antwort

0

Angenommen, Sie die Typen aufzählen können Sie Sie unterstützen möchten, können dies tun:

#include <iostream> 
#include <string> 
#include <vector> 

template <class... Ts> 
struct PseudoFunction { 
    private: 
    template <class T> 
    static void (*function)(T &); 

    template <class T> 
    static void call_func(void *object) { 
     return function<T>(*static_cast<T *>(object)); 
    } 

    template <class Fun> 
    static void assign(Fun) {} 

    template <class Fun, class Head, class... Tail> 
    static void assign(Fun fun) { 
     function<Head> = fun; 
     assign<Fun, Tail...>(fun); 
    } 

    public: 
    template <class T> 
    PseudoFunction(T *t) 
     : object(t) 
     , func(call_func<T>) {} 

    template <class F> 
    static void set_function(F f) { 
     assign<F, Ts...>(f); 
    } 
    void operator()() { 
     func(object); 
    } 

    private: 
    void *object; 
    void (*func)(void *); 
}; 

template <class... Ts> 
template <class T> 
void (*PseudoFunction<Ts...>::function)(T &) = nullptr; 

//example types that are not related and not copy constructible 
//but have the same member function name and signature 
struct T1 { 
    T1() = default; 
    T1(const T1 &) = delete; 
    void func(double d) { 
     std::cout << "T1: " + std::to_string(d) + '\n'; 
    } 
}; 

struct T2 { 
    T2() = default; 
    T2(const T2 &) = delete; 
    void func(double d) { 
     std::cout << "T2: " + std::to_string(d) + '\n'; 
    } 
}; 

int main() { 
    T1 t1; 
    T2 t2; 

    using PF = PseudoFunction<T1, T2>; 

    std::vector<PF> funcs; 
    funcs.push_back(&t1); 
    funcs.push_back(&t2); 

    PF::set_function([](auto &object) { object.func(3.14); }); 
    for (auto &f : funcs) { 
     f(); 
    } 
} 

(demo)

Es hat ordentliche Aufruf-Syntax (nur, dass Sie die Funktion angeben müssen, bevor die anruf Objekte) und etwas Overhead zum Setzen von potentiell unbenutzten Funktionszeigern.

Man könnte wahrscheinlich einen Wrapper machen, der die set_function tut und iteriert über die PF s in einem Rutsch.

Verwandte Themen