C func-ptr to C++ std::function



  • Guten Morgen Leute,

    folgends, ich habe eine c-lib mit eine "callback" funcptr.

    typedef VOID(*fpUpdatePolygon)(stPolygon_t* const pPolygon, const stObject_t input);
    
    void lib_register(fpUpdatePolygon fp);
    
    

    wobei der callback innerhalb der c-lib invoked wird....

    So nun habe ich eine C# Wrapper ,welcher folgendermaßen aussieht und auch funktioniert:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
               public delegate void fpUpdatePolygon(IntPtr polygon, DsmFieldStrategy.DsmFieldStrategyWrapper.stBuffer_t input);
    
               [DllImport("lib.dll", CallingConvention = CallingConvention.Cdecl)]
               public static extern void lib_register(fpUpdatePolygon fp);
    
           class foo
           {
               private fpUpdatePolygon _callback;
    
               private void UpdateNativePolygon(IntPtr pPolygon, stBuffer_t input)
               {
               ....... callback of pf
               }
    
               public foo()
               {
                   _callback= new fpUpdatePolygon(UpdateNativePolygon);
    
                   lib_register(_callback);
               }
           }
    
    

    nun möchte ich das ganze auch in C++ machen:

    class foo
    {
    	void UpdateNativePolygon(stPolygon_t* const pPolygon, const stObject_t input)
    	{
    		/* do something */
        }	
    
    	const std::function<void(stPolygon_t* const, const stObject_t)> _callback;
    
    public:
    
    	foo()
    		:_callback(std::bind(&foo::UpdateNativePolygon, this, placeholders::_1, placeholders::_2))
    	{
    
    		auto fp = _callback.target<fpUpdatePolygon>();
    
    		//_> fp ist hier null???
    
    		lib_register(fp);
    	}
    };
    

    aber ich werd nicht schlau, liefert mir Target nicht den nativen funcptr? fp ist an de stelle null? ich dachte die interop. ist zwischen c und c++ besser als mit c# 😉

    Was mache ich falsch?

    danke euch



  • Man erhält bei std::function<R(Args...)>::target nur den als Template angegeben Typ zurück, wenn dieser auch genauso abgespeichert wurde:

    A pointer to the stored function if target_type() == typeid(T), otherwise a null pointer.

    Es findet keine Konvertierung o.ä. statt, sondern dann wird einfach nur null zurückgegeben.
    Du musst schon einen C-Funktionszeiger verwenden (dann kannst du aber keine Memberfunktion angeben, außer diese ist static).



  • @Th69 sagte in C func-ptr to C++ std::function:

    Du musst schon einen C-Funktionszeiger verwenden (dann kannst du aber keine Memberfunktion angeben, außer diese ist static).

    hi, ja wenn ich die funktion static mache geht es ,aber würde gern eine member funktion verwenden, gnau so wie im obigen c# wrapper da geht es ja wunderbar.

    Gibt es keine möglichkeit ne member funktion auf den c fp zu casten/binden?



  • Nein, das geht bei C++ nicht. Bei einer nicht-statischen Memberfunktion wird immer implizit ein weiterer Parameter (this) erwartet.
    Da bei dir die C-Funktion keinen weiteren Parameter vom Typ void* verwendet, kann man nur globale Funktionen oder aber statische Memberfunktionen verwenden (ansonsten könnte man dann einen expliziten Cast benutzen).

    In C# wird durch die explizite Angabe von [UnmanagedFunctionPointer(...)] sichergestellt, daß der this-Parameter intern nicht mitübergeben wird.



  • @Th69 sagte in C func-ptr to C++ std::function:

    Nein, das geht bei C++ nicht. Bei einer nicht-statischen Memberfunktion wird immer implizit ein weiterer Parameter (this) erwartet.
    Da bei dir die C-Funktion keinen weiteren Parameter vom Typ void* verwendet, kann man nur globale Funktionen oder aber statische Memberfunktionen verwenden (ansonsten könnte man dann einen expliziten Cast benutzen).
    In C# wird durch die explizite Angabe von [UnmanagedFunctionPointer(...)] sichergestellt, daß der this-Parameter intern nicht mitübergeben wird.

    ok verstehe, aber we bekommt c# dann hin dass der der native fp in ein c# member pt mit this gerelayed wird? wenn da geht, müsste es ja auch in c++ geht?



  • Das habe ich mir gedacht, daß jetzt diese Frage kommt. 😉

    Antwort: Mittels "Compilermagie"!
    So viel ich weiß (bzw. mal gelesen habe), erzeugt der C#-Compiler eine eigene Datenstruktur mit einem Zeiger (IntPtr) auf eine aus der Memberfunktion erzeugten neuen Funktion, wo die Zugriffe auf die Membervariablen dann auf diese Datenstruktur umgebogen sind. Und vor dem Aufruf werden dann diese Variablen aus den Membervariablen kopiert und nach dem Aufruf wieder zurückkopiert (Marshalling).

    Gefunden habe ich dazu nur noch Proposal: C# should provide better interop with unmanaged function pointers (von 2017), wo es darum ging, daß C# direkt C-Funktionzeiger unterstützen soll (ohne Marshalling), was auch jetzt seit C# 9 (C# - Function Pointers for High-Performance Scenarios ) möglich ist.

    Das ist der Vorteil von Sprachen mit Reflection bzw. dynamischer Codererzeugung (wobei ich nicht genau weiß, ob dies direkt beim Compilieren oder aber einmalig zur Laufzeit erzeugt wird).
    Dies ginge sicherlich auch mit C++, aber bisher unterscheidet C++ eben streng zwischen Funktionszeigern (R (*)(...)) und Memberfunktionszeigern (R (C::*)(...)).
    Dafür bräuchte man wohl dann ein eigenes Schlüsselwort - und außerdem setzt C++ (wie C) auf hohe Performance. std::function<...> ist dabei ein Kompromiss um einen Datentyp zu haben, welcher beides unterstützt - aber eben keine Konvertierung ermöglicht.

    Wenn du es bei C# mal genauer wissen willst, dann könntest du einen C# Compiler wie SharpLab benutzen und dir den generierten Code bzw. IL-Code anschauen.



  • Könnte man eventuell was mit lambdas machen, welche den "this" Pointer capturen um dann instanz methoden aufrufen zu können.
    Oder ist eine lambda nicht kompatibel zu einem funktions-pointer?



  • @firefly

    Lambdas gehen nur ohne Capture 😉



  • @Schlangenmensch sagte in C func-ptr to C++ std::function:

    @firefly

    Lambdas gehen nur ohne Capture 😉

    Schade. Hätte ja sein können das ein lambda mit capture auch kompatible zu einem funktion pointer wäre



  • @Th69 sagte in C func-ptr to C++ std::function:

    Das habe ich mir gedacht, daß jetzt diese Frage kommt.
    Antwort: Mittels "Compilermagie"!
    So viel ich weiß (bzw. mal gelesen habe), erzeugt der C#-Compiler eine eigene Datenstruktur mit einem Zeiger (IntPtr) auf eine aus der Memberfunktion erzeugten neuen Funktion, wo die Zugriffe auf die Membervariablen dann auf diese Datenstruktur umgebogen sind. Und vor dem Aufruf werden dann diese Variablen aus den Membervariablen kopiert und nach dem Aufruf wieder zurückkopiert (Marshalling).
    Gefunden habe ich dazu nur noch Proposal: C# should provide better interop with unmanaged function pointers (von 2017), wo es darum ging, daß C# direkt C-Funktionzeiger unterstützen soll (ohne Marshalling), was auch jetzt seit C# 9 (C# - Function Pointers for High-Performance Scenarios ) möglich ist.
    Das ist der Vorteil von Sprachen mit Reflection bzw. dynamischer Codererzeugung (wobei ich nicht genau weiß, ob dies direkt beim Compilieren oder aber einmalig zur Laufzeit erzeugt wird).
    Dies ginge sicherlich auch mit C++, aber bisher unterscheidet C++ eben streng zwischen Funktionszeigern (R ()(...)) und Memberfunktionszeigern (R (C::)(...)).
    Dafür bräuchte man wohl dann ein eigenes Schlüsselwort - und außerdem setzt C++ (wie C) auf hohe Performance. std::function<...> ist dabei ein Kompromiss um einen Datentyp zu haben, welcher beides unterstützt - aber eben keine Konvertierung ermöglicht.
    Wenn du es bei C# mal genauer wissen willst, dann könntest du einen C# Compiler wie SharpLab benutzen und dir den generierten Code bzw. IL-Code anschauen.

    erstmal "Chapeau", was du so ausm stehgreif weißt;)

    zum glück habe ich die c- codebase in der hand.. ich werde jetzt dem callback ein "context/tag" void* mit geben.

    
    lib_register(this,callback)
    
    

    dann geht das auch so;)


Log in to reply