2016-11-24 18 views
1

Ich habe eine Klasse:Wrap C++ Methode mit einer C Methode

class C { 
public: 
    int func1 (int arg1, int arg2) { return 1; } 
    float func2 (float arg1, float arg2) { return 2; } 
} ; 

Ich mag eine C Wrapper-Funktion für die Methoden und func1 func2 erstellen und dann in einen Leerfunktionszeiger gegossen.

typedef void (*Function)(void); 
typedef Function *FunctionPtr; 

FunctionPtr functions[] = { 
    (FunctionPtr)&Wrap<&C::func1>::f, 
    (FunctionPtr)&Wrap<&C::func2>::f 
}; 

Nach dem ich konnte sie auf C Funktionszeiger zurückgeworfen und verwenden sie:

Class C c; 

typedef int (*MyIntFunction)(C *c, int arg1, int arg2); 
MyIntFunction *mif = (MyIntFunction *)functions[0]; 
int ri = (*mif)(&c, 1, 2); 

typedef float (*MyFloatFunction)(C *c, float arg1, float arg2); 
MyFloatFunction *mff = (MyFloatFunction *)functions[1]; 
float rf = (*mff)(&c, 1, 2); 

ich mit verschiedenen Templat magics experimentiert haben, aber nicht der richtige Weg, so weit dies zu tun gefunden .

+2

Wie erwarten Sie, eine Klasse in C zu deklarieren? –

+1

Das Übergeben von Funktionszeigern an Datenzeiger ('void *') ist ein nicht definiertes Verhalten. – StoryTeller

+0

Warum möchten Sie das tun?Besteigen Sie eine gemeinsam genutzte Bibliothek und möchten eine C-Schnittstelle zur zugrunde liegenden C++ - Implementierung? – Chad

Antwort

4

Sie können nicht Klassen oder Vorlagen in C-Code verwenden. Um eine C++ - Funktion zu umbrechen, müssen Sie einen C-Wrapper für jede C++ - Funktion erstellen. In einem Header:

#ifdef __cplusplus 
extern "C" { 
#endif 

int C_func1(void *obj, int arg1, int arg2); 

#ifdef __cplusplus 
} 
#endif 

In einem .cpp:

#ifdef __cplusplus 
extern "C" { 
#endif 

int C_func1(void *obj, int arg1, int arg2) { 
    C *const that = static_cast<C*>(obj); 
    return that->func1(arg1, arg2); 
} 

#ifdef __cplusplus 
} 
#endif 

Wiederholen für C::func2, und so weiter.

+0

Sie können auch ein Makro mit variadischer Argumentliste verwenden, um die Erstellung dieser Wrapper-Funktionen zu vereinfachen. –

+1

@ Ville-ValtteriTiittanen Sie würden 4 Versionen des Makros benötigen: (1) No-Return mit No-Args (2) Return mit No-Args (3) No-Return mit Args (4) Return mit Args. –

+0

Während dies eine Lösung für "Erstellen eines Wrappers für eine C++ Funktion mit einer c-Funktion" ist, "es" nicht automatisieren "mit der Magie der Vorlagen in jedem Fall. Ich suche nach einer Lösung, wo ich sagen kann: "(FunctionPtr) & Wrap <&C::func1> :: f" oder etwas ähnliches. (Wie ich im zweiten Satz der Frage ausgeführt habe). Ich erstelle eine Funktionstabelle im C++ - Code und verwende diese Funktionstabelle dann im C-Code. - Ich werde das nicht als richtige Antwort bezeichnen. – iamacomputer

0

Okay, ich werde meine Antwort auf meine "nicht-so-großartige-Lösung" posten. Ich hoffe wirklich auf etwas Besseres als meines.

template<typename R, typename C, typename... A> 
struct MemberFunctionPointer 
{ 
    typedef R Return; 
    typedef C Class; 
}; 

template<typename R, typename C, typename... A> 
constexpr auto inferMemberFunctionPointer(R (C::*method)(A...)) 
{ 
    return MemberFunctionPointer<R,C,A...>{}; 
} 

template<typename M, M m, typename... A> 
class WrapMemberFunction 
{ 
    typedef typename decltype(inferMemberFunctionPointer(m))::Class T; 
    typedef typename decltype(inferMemberFunctionPointer(m))::Return R; 
public: 
    static R f (T *t, A... args) { 
     return (t->*m)(args...); 
    } 

} ; 

#define WrapMember(TM) (FunctionPtr)&WrapMemberFunction<decltype(&TM), &TM>::f 
#define WrapMemberA(TM, args...) (FunctionPtr)&WrapMemberFunction<decltype(&TM), &TM, args>::f 

Ich glaube, das bewirkt, dass der Compiler:

  1. schließen die Rückkehr und die Klasse (natürlich)
  2. eine spezielle Template-Klasse erstellen, die eine statische Funktion hat, die die Memberfunktion
  3. angegeben ruft
  4. kann diese spezielle statische Template-Klassenfunktion als exportierbare C-Funktion referenziert werden.

Und man kann die Dinge schreiben wie:

FunctionPtr f = WrapMember(MyClass::MyFunction); 

Leider gibt es in meinem Ansatz ist das Makro notwendig, was Ich mag es nicht.

Ich habe viele Dinge ausprobiert, dieses Makro Ohne Erfolg .. loszuwerden, wie folgt aus:

template<typename R, typename C, typename... A> 
constexpr auto binderX(R (C::*m)(A...)) 
{ 
    typedef decltype(m) M; 
    return &WrapMemberFunction<M, m, A...>::f; 
} 

FunctionPtr _x = binderX(&SimpleVector::printOther); 
+0

Haben Sie tatsächlich versucht, eine dieser eingepackten Funktionen aufzurufen? Funktioniert es tatsächlich? –

+0

Ja. Sie arbeiten. – iamacomputer

0

Nach zahlreichen Tests ich endlich gekommen sind, glaube ich, die einfachste Art und Weise der erreichen, was ich will:

template<typename M, M m, typename T, typename... A> 
class WrapMemberFunctionV 
{ 
public: 
    static void f (T *t, A... args) { 
     (t->*m)(args...); 
    } 
} ; 

template<typename M, M m, typename R, typename T, typename... A> 
class WrapMemberFunction 
{ 
public: 
    static R f (T *t, A... args) { 
     return (t->*m)(args...); 
    } 
} ; 

template<typename M, M m, typename R, typename C, typename... A> 
constexpr auto generateCompileTimeWrapper(R (C::*method)(A...)) 
{ 
    return &WrapMemberFunction<M, m, R, C, A...>::f; 
} 

template<typename M, M m, typename C, typename... A> 
constexpr auto generateCompileTimeWrapper(void (C::*method)(A...)) 
{ 
    return &WrapMemberFunctionV<M, m, C, A...>::f; 
} 

#define WrapMethod(TM) generateCompileTimeWrapper<decltype(&TM), &TM>(&TM) 

Verwendung wie folgt:

auto m = WrapMethod(MyClass::myMethod); 

für meine Zwecke, muss ich erkennen, ob es eine ist Rückgabewert. (statt void). Wenn Sie dafür keinen speziellen Code benötigen, können Sie WrapMemberFunctionV entfernen und einfach den anderen verwenden. für beide keine Rückgabe (void) und Rückgabewert.