2009-07-15 12 views
6

Ich bin im Grunde um einen Wrapper für eine generische C-Funktion zu generieren, ohne die Typen manuell angeben zu müssen. Also habe ich einen Callback mit einem festen Prototyp, aber ich werde einen speziellen Code im Wrapper basierend auf dem Typ der eingepackten Funktion machen müssen ... Also denke ich im Grunde daran, eine statische Methode in einer Klassenvorlage zu verwenden wrap meine Funktion auf eine konforme Schnittstelle zB:Nicht-Typ Vorlagenparameter ... das ist eine Vorlage! (C++)

// this is what we want the wrapped function to look like 
typedef void (*callback)(int); 
void foobar(float x); // wrappee 

// doesn't compile 
template< T (*f)(S) > // non-type template param, it's a function ptr 
struct Wrapper 
{ 
    static void wrapped(int x) 
    { 
    // do a bunch of other stuff here 
    f(static_cast<S>(x)); // call wrapped function, ignore result 

    } 
} 

und dann würde ich mag, wie etwas zu tun ist:

AddCallback(Wrapper<foobar>::wrapped); 

Allerdings ist das Problem, dass ich einfach nicht voran gehen und Verwendung ein "S" im Parameter der Funktion in der Wrapper Vorlage, muss ich es zuerst als Parameter auflisten:

template< class T, class S, T (*f)(S) > 
struct Wrapper 
// ... 

Aber das bedeutet, es ist viel schmerzhafter zu verwenden (Wrapper<void,float,foobar>::wrapped), idealerweise möchte ich einfach den Funktionszeiger dort übergeben und es die Typen der Parameter (und Rückgabetypen) automatisch erarbeiten lassen. Um klar zu sein, muss ich mich innerhalb der eingepackten Funktion auf die Typen des Funktionszeigers beziehen (also brauche ich ein Äquivalent von S oder T).

Gibt es eine Möglichkeit, dies zu tun?

+0

@damndirtyape: Ich habe über deine Frage nachgedacht, und ich denke, ich habe etwas ähnliches getan. Leider war viel Code nötig. Im Grunde hatte meine Lösung eine Basisklasse, die operator() überladen hat, und ich hatte Factory-Funktionen, die basierend auf dem übergebenen Funktionstyp Basisklassen daraus erstellten. Wenn Sie möchten, kann ich den Code irgendwo in einem Pastebin posten. –

Antwort

0

Wenn Sie eine Funktion verwenden, die das "Wrapped" zurückgibt, anstatt direkt auf es zu verweisen, wird der Compiler versuchen, die Vorlagenparameter für den Funktionsaufruf automatisch abzugleichen.

edit: Was ist damit?

int foobar(float x); // wrappee 

template <typename T, typename S> 
struct Wrapper { 
    typedef T (*F)(S); 
    F f; 

    Wrapper(F f) : f(f) { } 

    void wrapped(S x) { 
     // do a bunch of other stuff here 
     f(x); // call wrapped function, ignore result 
    } 
}; 

template <typename T, typename S> 
Wrapper<T,S> getWrapper(T (*f)(S)) { 
    return Wrapper<T,S>(f); 
} 

... 
getWrapper(foobar).wrapped(7); 
+0

Das löst das Problem nicht, weil jetzt die Template-Parameter für diese Funktion das gleiche Problem haben. – damndirtyape

+0

Sind Sie sicher? Vielleicht verstehe ich einfach nicht, was Sie versuchen zu tun ... –

+0

Funktioniert nicht, weil Sie die Funktion von einer statischen Methode zu einer Instanzmethode geändert haben, aber der ganze Punkt war, die verpackte Funktion konform zu machen eine vorhandene Callback-Signatur (was bedeutet, dass sie statisch sein muss). – damndirtyape

0

EDIT: völlig neue Antwort

OK, habe ich komplett neu gedacht, um die Frage und glauben, dass ich das bekommen, was Sie wollen. Ich habe das schon mal gemacht :-P.

Hier ist die Idee, ich habe eine Basisklasse, die Operator() überlädt, dann habe ich eine Unterklasse für jede "Arity" von Funktionen. Schließlich habe ich eine Factory-Funktion, die eines dieser Dinge zurückgibt. Der Code ist groß (und wahrscheinlich ein wenig übertrieben), funktioniert aber gut. Viele der library_function Überladungen sollen verschiedene, meist unnötige Syntaxen unterstützen. Es unterstützt auch boost::bind Funktionen, Mitgliedsfunktionen, usw., sehr viel mehr als Sie wahrscheinlich brauchen.

http://pastebin.com/m35af190

Beispiel, Nutzung:

// map of library functions which will return an int. 
std::map<std::string, LibraryFunction<int> > functions; 

// function to register stuff in the map 
void registerFunction(const std::string &name, LibraryFunction<int> func) { 
    functions.insert(std::make_pair(name, func)); 
} 

später können Sie dies tun:

// the this param is so the function has access to the scripting engine and can pop off the parameters, you can easily chop it out 

// register 2 functions, one with no params, one with 1 param 
registerFunction("my_function", library_function1(*this, call_my_function)); 
registerFunction("my_function2", library_function0(*this, call_my_function2)); 

functions["my_function"](); 
functions["my_function2"](); 
+0

Dies lässt mich nicht eine vorhandene Funktion umbrechen, ich muss ein neues Objekt schreiben, was bedeutet, dass ich noch die Typen vorne angeben muss, anstatt sie abzuleiten? – damndirtyape

+0

Ich sehe nicht, wie mir das hilft. Im Grunde habe ich eine spezifische Callback-Signatur (auf die ich keine Kontrolle habe), ich muss eine große Anzahl existierender C-Funktionen einbinden, um dieser Callback-Signatur zu entsprechen (es ist eine script-bindende Sache, also muss ich für eine 3-Parameter-Funktion poppen) 3 Werte aus dem Stapel, übergeben Sie sie an die Hülle und drücken Sie das Ergebnis wieder auf den Stapel - aber extern jede einzelne Funktion hat die exakt gleiche Signatur. – damndirtyape

+0

Noch einmal, das hilft mir nicht. Ich muss es in einen Funktionszeiger verwandeln, der mit einer festen Callback-Signatur kompatibel ist. Das macht das nicht. Ich muss eine Funktion umdrehen, die eine Saite (etwa) zu einer Funktion, die dem Callback entspricht, macht, indem ich in der Funktion etwas magisch mache (die Saite zu einem Stack o.ä. drängen), aber die Signatur dieser eingepackten Funktion * muss * entsprechen dem Rückruf. – damndirtyape

0

ich boost aussehen würde. Beim ersten Lesen Ihrer Frage scheint es mir als <boost/function_types/parameter_types.hpp> was Sie brauchen.

+0

Keine wirklich gute Antwort: Ohne zumindest auf die betroffene Boost-Bibliothek zu verweisen, ist es so, als würde man sagen "Suche bei Google, ich denke, die Antwort liegt dort". Außerdem sehe ich in Boost nichts, was die OP-Anforderungen erfüllen könnte. Könnten Sie in Ihrer Antwort genauer sein? –

+0

hatte ich. Ich hatte direkt <und> anstelle von HTML-Entities verwendet und der Header-Name wurde ausgeblendet. Ich habe meine Antwort bearbeitet, um das zu korrigieren. – AProgrammer

+0

Ein in Kommentaren, man braucht die Entitäten nicht und < and > funktioniert ... nun, ich lerne immer noch die Schnittstelle. – AProgrammer

5

Eine Sache, die Sie in Betracht ziehen sollten, ist die Verwendung von LLVM oder ähnlichem, um eine geeignete Trampolin-Funktion zur Laufzeit zu erzeugen.Oder hier ist eine statische Lösung:

#include <iostream> 

void f(float f) { std::cout << f << std::endl; } 

template<typename T, typename S> struct static_function_adapter { 
     template<T(*f)(S)> struct adapt_container { 
       static void callback(int v) { 
         f(static_cast<S>(v)); 
       } 
     }; 

     template<T(*f)(S)> adapt_container<f> adapt() const { 
       return adapt_container<f>(); 
     } 
}; 

template<typename T, typename S> struct static_function_adapter<T, S> get_adapter(T (*)(S)) { 
     return static_function_adapter<T, S>(); 
} 

#define ADAPTED_FUNCTION(f) (&get_adapter(f).adapt<f>().callback) 

int main() { 
     void (*adapted)(int) = ADAPTED_FUNCTION(f); 
     adapted(42); 
     return 0; 
} 

Die get_adapter Funktion uns das Argument und Rückgabetyp ableiten läßt; adapt() konvertiert dies dann in einen Typ, der für die tatsächliche Funktion parametrisiert ist, und schließlich erhalten wir eine statische Funktion im Callback.

+0

Ich habe nur keine Ahnung, was dieser Code machen soll und welches Problem er lösen soll: O – Virus721

Verwandte Themen