Proxy-Function per Template erzeugen
-
template<typename FPtrT, FPtrT Fun, typename... Args> auto proxy_function(Args&&... args) -> decltype(Fun(args...)) { return Fun(std::forward<Args>(args)...); } int bla() { return 1; } int bla(bool) { return 2; } int bla(float) { return 3; } int blo(float, int, bool) { return 4; } int main() { proxy_function<int(*)(), &bla>(); proxy_function<int(*)(bool), &bla>(true); proxy_function<int(*)(float), &bla>(1.23); proxy_function<decltype(&blo), &blo>(1.23, 1, true); }Habe es währenddessen genau so selbst gemacht, danke.

Hat die Lösung einen Haken?
-
Du kannst es sogar durch ein Makro noch weiter vereinfachen:
#define proxy_function(foo) proxy_function<decltype(&(foo)), &(foo)>
-
Mal wieder die Frage: Was ist das Ziel der Aktion? Was willst Du erreichen?
-
Ethon schrieb:
ist es möglich eine Art Proxyfunktion per Template zu erzeugen?
std::function?

int bla(float blo) { return (int)blo + 3; } int main() { std::function<int(float)> bla_proxy(&bla); auto i = bla_proxy(3.14f); }
-
Tachyon schrieb:
Mal wieder die Frage: Was ist das Ziel der Aktion? Was willst Du erreichen?
Ich schreibe einen Wrapper für eine Script-API, die logischerweise die Calling-Convention kennen muss, um nativen Code aufrufenzu können. Um den User von diesem Detail zu abstrahieren, möchte ich sicherstellen, dass alle Funktionen die selbe (mir bekannte) Calling-Convention haben. Es ist ja meines Wissens nach leider nicht möglich mit Compilertricks zur Laufzeit die Calling-Convention einer Funktion zu bestimmen, void()() attribute((__cdecl__)) ist vom Typ her identisch zu void()() attribute((__stdcall__))
@pumuckl: Da hängt ja leider ein Objekt dran.
-
Ethon schrieb:
@pumuckl: Da hängt ja leider ein Objekt dran.
das hängt bei Dir auch dran:
register(&proxy_function<&MessageBoxA>);In Deinem Fall ist das Objekt ein Funktionszeiger. Den kannst Du aber eigentlich sehr gut durch ein Funktionsobjekt ersetzen.
-
Nachdem der Compiler das Template aufgelöst hat, hängt da nichts mehr dran. Sind ja alles Compilezeitkonstanten.
int (*fun)(float) = &function_proxy<decltype(&bla), &bla, float>; fun(12.3);Bei std::function ist das NICHT so.
std::function<int (float)> fun(bla); fun(12.3);ist mehr oder weniger äquivalent zu
std::function<int (float)> fun(bla); int (std::function<int (float)>::* op)(float) = &std::function<int (float)>::operator(); fun.*op(12.3);
-
Ethon schrieb:
Nachdem der Compiler das Template aufgelöst hat, hängt da nichts mehr dran. Sind ja alles Compilezeitkonstanten.
Nein. In dem einen Fall ist das Objekt ein pointer:
int (*fun)(float) = &function_proxy<decltype(&bla), &bla, float>; //fun ist ein Objekt! vom Typ int(*)(float) fun(12.3);Ethon schrieb:
Bei std::function ist das NICHT so.
Doch:
std::function<int (float)> fun(bla); //fun ist ein Objekt! vom typ function<int(float)> fun(12.3);In beiden Fällen hast Du ein Objekt, welches das Delegate für die eigentliche Funktion darstellt.
Ethon schrieb:
ist mehr oder weniger äquivalent zu
std::function<int (float)> fun(bla); int (std::function<int (float)>::* op)(float) = &std::function<int (float)>::operator(); fun.*op(12.3);Nein, das ist dann nicht mehr wirklich äquivalent, da es UB ist bzw. gar nicht kompiliert.
Du willst ein Delegate, wo die Callingconvention nicht mehr interessiert. Ein Funktionszeiger ist ein primitves Delgate. std::function eine etwas komplexeres, aber dafür auch leistungsfähigeres Delegate.
Wo wäre das Problem, wenn die Funktion zum Registrieren Deines Delegates ein std::function-Objekt nimmt?Mit einer Funktion
void register_fun(std::function<int (HWND, const char*, const char*, unsigned)> const & fun);könntest Du dann direkt den Aufruf mit
register_fun(&MessageBoxA);machen.
-
Tachyon schrieb:
Ethon schrieb:
Nachdem der Compiler das Template aufgelöst hat, hängt da nichts mehr dran. Sind ja alles Compilezeitkonstanten.
Nein. In dem einen Fall ist das Objekt ein pointer:
Dann formuliere ich es um: Es ist eine freie Funktion, keine Memberfunktion, die einen Objektzeiger benötigt.
Tachyon schrieb:
Nein, das ist dann nicht mehr wirklich äquivalent, da es UB ist bzw. gar nicht kompiliert.
Abzüglich Syntaxfehler, ich habe noch nie mit Memberfunctionpointern gearbeitet: Es ist UB, einen Zeiger auf eine Memberfunktion zu nehmen? Wieso ist das Feature dann überhaupt im Sprachstandard?
Tachyon schrieb:
Du willst ein Delegate, wo die Callingconvention nicht mehr interessiert. Ein Funktionszeiger ist ein primitves Delgate. std::function eine etwas komplexeres, aber dafür auch leistungsfähigeres Delegate.
Wo wäre das Problem, wenn die Funktion zum Registrieren Deines Delegates ein std::function-Objekt nimmt?Mit einer Funktion
void register_fun(std::function<int (HWND, const char*, const char*, unsigned)> const & fun);könntest Du dann direkt den Aufruf mit
register_fun(&MessageBoxA);machen.
Hast du dich schon mal damit beschäftigt, wie Funktionsaufrufe im Maschienen code aussehen?
operator() ist eine Memberfunktion, bei der anderen Lösung ist es eine freie Funktion. Eine Memberfunktion lässt sich logischerweise NIE aufrufen, ohne das Objekt als Parameter mit zu übergeben. (Mit der Ausnahme von Memberfunktionen, die auf keine Klassendaten zugreifen).Die API ruft die Funktionen manuell auf, dh. die Argumete werden auf den Stack/in Register gelegt, die Funktion wird aufgerufen, das Resultat wird kopiert und der Stack aufgeräumt. Dazu muss die API eben wissen, ob sie da ein cdecl/stdcall/fastcall oder was auch immer bekommt. Ich möchte immer die selbe Calling Convention.
Bei std::function ist ja allein schon das Problem, dass die Signatur anders ist (Wegen dem impliziten this-Pointer) und zur Laufzeit kein Objekt da ist. Desweiteren müsste Support für Memberfunktion (bei x86 Windows thiscall, bei x86 Unix cdel mit dem this-pointer als ersten Parameter etc) bereitgestellt werden.
-
Was soll denn Deine "register"-Funktion machen? Was ist der Parameter Deiner "register"-Funktion? Und wie soll das, was da registriert wurde, benutzt werden? Du hast eben aus sicht dessen, was da registriert wurde keine freie Funktion, sondern einen Zeiger auf eine freie Funktion.
Vielleicht erlärst Du auch noch mal, was Du genau erreichen willst. Wer ruft die API auf, wer soll das Delegate aufrufen, und was soll durch den Aufruf des Delagates erreicht werden? Wer ist "der User" vor dem irgendwelche Calling-Conventions abstahiert werden sollen, und was bekommt "der User" von Deinem Gesamtwerk zu sehen?
-
Tachyon schrieb:
Was soll denn Deine "register"-Funktion machen? Was ist der Parameter Deiner "register"-Funktion? Und wie soll das, was da registriert wurde, benutzt werden? Du hast eben aus sicht dessen, was da registriert wurde keine freie Funktion, sondern einen Zeiger auf eine freie Funktion.
Vielleicht erlärst Du auch noch mal, was Du genau erreichen willst. Wer ruft die API auf, wer soll das Delegate aufrufen, und was soll durch den Aufruf des Delagates erreicht werden? Wer ist "der User" vor dem irgendwelche Calling-Conventions abstahiert werden sollen, und was bekommt "der User" von Deinem Gesamtwerk zu sehen?
Es ist etwas Ähnliches wie Boost.Python.
Der User übergibt meiner Wrapper-Bibliothek einen Namen und einen Funktionszeiger und kann die Funktion innerhalb eines Scriptes über den gegebenen Name aufrufen.
Die Scriptbibliothek darunter benötigt tiefergehende Informationen über die Funktion, um sie korrekt aufrufen zu können. Eine dieser Informationen ist die Calling Convention. Ohne dieser Wrapper-Funktionen, die ich per Template generiere, müsste ich den User bei jeder Funktion fragen, welche Calling Convention er da hat. So kann ich immer eine bestimmte Calling-Convention forcieren.
-
Achso, die Script-Sprache selbst kommt nicht von Dir?
-
Jup.