Im Moment baue ich Funktoren (Callable-Typen) für die verschiedenen Aufrufkonventionen (__stdcall, __cdecl, __fastcall etc.). Mit den Wrapper werde ich in der Lage sein, so etwas zu tun:Callable Objekte mit verschiedenen Aufrufkonventionen
void __stdcall foo(int arg)
{
std::printf("arg: %i\n", arg);
}
int main(int, char**)
{
Function<void, int> v{foo};
v(1337);
return EXIT_SUCCESS;
}
Im Moment habe ich einen Wrapper für die Aufrufkonvention __stdcall aufgebaut haben, die jede __stdcall Funktion so lange zu nennen ist in der Lage, da die korrekten Parameter angegeben werden und die richtigen Argumente übergeben werden in der Klasse sieht wie folgt aus:.
template <typename ReturnT, typename... Args>
class Function
{
// NOTE: This version of my callable types
// only supports the __stdcall calling
// convention. I need support for __cdecl,
// __fastcall and also __thiscall.
using return_t = ReturnT;
using callable_t = return_t(__stdcall*)(Args...);
private:
callable_t mCallable;
public:
template <typename FuncT>
Function(FuncT const &func) :
mCallable(func)
{
;
}
void operator()(Args&&... args)
{
mCallable(std::forward<Args>(args)...);
}
};
Damit in der Hand habe ich beschlossen, die anderen Wrapper zu bauen, aber ich dachte, dass das gleiche Stück Code eingeben und die Änderung der aufruf~~POS=TRUNC innerhalb von Die using-Deklaration für callable_t ist mehr Arbeit als nötig. Also wollte ich einen Weg finden, ungefähr 4 Varianten von aufrufbaren Typen zu erstellen (für jede Aufrufkonvention), aber ich konnte keinen Weg finden, dies zu tun.
Bisher habe ich versucht, eine ENUM als nicht-Typ Template-Parameter wie folgt zu verwenden:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
// ...
};
Aber ich weiß nicht, wie der Typ des Anruf Objekt iterieren und stellen den gewünschten Typ (I versuchte mit std :: is_same/std :: enable_if, aber das war eine Sackgasse). Ich habe auch versucht Template-Spezialisierung mit Code wie folgt:
struct StdcallT { ; };
struct CdeclT { ; };
struct FastcallT { ; };
template <typename CallT>
struct BaseT { };
template <> struct BaseT<StdcallT> { using CallableT = void(__stdcall*)(); };
template <> struct BaseT<CdeclT> { using CallableT = void(__cdecl*)(); };
template <> struct BaseT<FastcallT> { using CallableT = void(__fastcall*)(); };
template <typename CallT>
class Function
{
using CallableT = typename BaseT<CallT>::CallableT;
};
Aber ich dachte nicht an den Rest der Argumente (Rückgabetyp + Parameter), so kann dies auch nicht funktionieren.
Also hat sowieso irgendwelche Ideen, was ich tun kann? Eine Methode, ich denke an einen Schalter auf den Nicht-Typ-Parameter zu tun und die richtigen wie folgen anrufen:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
void operator()(ArgsT&&... args)
{
switch(Call)
{
case CallingConvention::Cdecl:
// Call a __cdecl version
break;
case CallingConvention::Stdcall:
// Call an __stdcall version
break;
// And so on...
}
}
};
Und trotzdem sieht aus wie eine funktionierende Lösung ich mich gefragt, ob es einige gute Alternativen war, dass Daran denke ich nicht.
Irgendwelche Ideen?
Warum das Rad neu erfinden? 'std :: function' sollte in der Lage sein, jeden Funktionszeiger oder Funktor unabhängig von der Aufrufkonvention zu speichern, solange sie mit den von Ihnen angegebenen Argumenten aufgerufen werden kann. Funktioniert es nicht für Sie, oder hat es einige konkrete Nachteile, die Sie zu Ihrer benutzerdefinierten Implementierung geführt haben? – hvd
@hvd Nehmen wir an, Sie haben eine std :: unordered_map Mapping-Zeichenfolgen wie "glCreateProgram" und "glCreateShader" geladen von opengl32.dll zu ihren Symboladressen. Sie möchten eine Template-Funktion function_cast erstellen, die Sie void für no args/returns und std :: tuple für args/no args verwenden, da sie binärkompatibel sind. So wie es aussieht, funktioniert std: function nicht für stdcall, daher wird eine Laufzeitprüfung benötigt, wenn eine Dummy-Aufrufkonvention erforderlich ist, auf der Sie das Aufrufkonvention-Casting umschalten. –
Dmitry
@Dmitry "Std: Funktion funktioniert nicht für Stdcall" - Ältere Versionen von GCC (bis GCC 5) ignoriert die Aufrufkonvention in Name Mangling, Linker Fehler verursachen, aber das wurde Mitte 2015 behoben, und es sollte nicht Probleme mit "std :: function" für nicht standardmäßige Aufrufkonventionen seit dann zumindest in diesem Compiler. Andere Compiler * sollten auf die gleiche Weise arbeiten. Wenn nicht, können Sie angeben, um welche es sich handelt? – hvd