I have a C++ class that I want to expose to C.
class Foo {
void Method();
}
Bridge to C
typedef struct Foo CFoo;
#ifdef __cplusplus
extern "C" {
#endif
CFoo * CFooInit();
void CFooRelease(CFoo * v);
void CFooMethod(CFoo * v);
#ifdef __cplusplus
}
#endif
and in cpp file, I have
CFoo * CFooInit(){
return new Foo();
}
void CFooRelease(CFoo * v){
delete v;
}
void CFooMethod(CFoo * v){
v->Method();
}
All is working OK, but there is one problem / warning during compilation.
'Foo': type name first seen using 'struct' now seen using 'class'
I know, why it is there, but I dont know, how to fix it. I cannot change the class to struct and I cannot have class keyword in C code. I can use void *, but it can lead to type unsafety.
You can't do that. As Lightness likes to say, there are no structs in C++; there are only classes. They may be represented differently.
You can only define extern "C" free functions in the C++ part which get a pointer to the class and expose those to the C code.
Intead of having a CFooInit return a CFoo*, used a void*. Similarly for CFooRelease, CFooMethod. Forget the typedef struct Foo CFoo.
Of course, those functions will have to cast between void* and Foo*, but that's the price you pay for mixing different type systems.
There's no value in pretending that there's something to be gained by having a C type that's somehow derived from a C++ type.
Related
I'm writing a library whose functionalities are provided in a C-compatible header like the following:
// File: bar.h
typedef struct bar_context_t bar_context_t;
#ifdef __cplusplus
extern "C" {
#endif
bar_context_t* bar_create();
void bar_destroy(bar_context_t*);
int bar_do_stuff(bar_context_t*);
#ifdef __cplusplus
}
#endif
and implemented in C++:
// File: bar.cpp
namespace bar {
class Context {
private:
// ...
public:
int do_stuff();
};
}
The problem is how to connect bar::Context to bar_context_t so I can implement the extern C functions.
I can think of two options:
Option A: Using
using bar_context_t = bar::Context;
Option B: Empty Derived Class
struct bar_context_t : public bar::Context {};
Which option is better? Or is there a better third option?
Neither is necessary (and I don't even think your first variant works; using is just C++ syntax sugar, and does not introduce a type).
You just declare bar_context_t to be a pointer to bar::Context; that's it. In C, you declare it to be a void*, since C has no type safety anyways.¹
¹strictly speaking, C has type safety, between the two types "function pointer" and "not a function pointer".
ultimately my goal is to write a Qt class(my class inherits from QObject and uses Q_OBJECT macro), then build a dll from it. then in python use ctype to access that dll.
when I write the dll normally, method names get mangled so I can't use them directly. instead I have to use getattr which I don't want to use(because others may need to use the dll and I don't want them to get confused).
anyway I read that to stop name mangle I need to use extern "C"
so far I tried these:
extern "C"
{
class MyClass::public QObject
{
Q_OBJECT
public:
void SomeFunction(int Parameter);
}
}
this one does not build because in Q_OBJECT macro templates are used and I get "templates cannot be declared to have 'C' linkage" error.
i tried:
extern "C" typedef void SomeFunction_t(int parameter);
class MyClass::public QObject
{
Q_OBJECT
public:
SomeFunction_t SomeFunction;
}
this one compiled but still SomeFunction name was mangled;
also I can't use extern "C" directly before method declaration because it is inside of class.
so how can I prevent name mangling here?
I'm going to hazard a guess this might be failing because of this construct:
extern "C"
{
class I_AM_A_CPLUSPLUS_CONCEPT {};
}
Anyhow, this is solvable, but requires a little rethink. To expose the class to C, you will have to jump through a couple of hoops here.
// the C++
class MyClass : public QObject
{
Q_OBJECT
public:
void SomeFunction(int Parameter);
};
extern "C" MyClass* MyClass_new() {
return new MyClass;
}
extern "C" void MyClass_delete(MyClass* c) {
delete c;
}
extern "C" void MyClass_SomeFunction(MyClass* c, int p) {
c->SomeFunction(p);
}
'MyClass' will be mangled into a C++ name, and there isn't really any way to expose 'SomeFunction' as a member of MyClass, without using name mangling.
C-Name mangling in the above example will simply expose:
MyClass_new, MyClass_delete, and MyClass_SomeFunction as the symbol names. (i.e. devoid of the function argument types, return type, calling convention, owning class, etc... which is what C++ name mangling will add in).
Just treat MyClass as an opaque pointer, and C code should be happy enough with that. Short of resorting to pybind/boost::python (which clearly don't really work with DLL's cleanly!!), there aren't really any other nice ways to bind in a C++ object to c code.
I have a typically declared class:
class Task {
public:
int test(int &c);
};
int Task::test(int &c) {
c += 8;
return c;
}
For compiling to WASM, I was trying to instantiate this class and call the test method, like so:
extern "C" {
Task t;
int scratch() {
int c = 123;
t.test(c);
return c;
}
}
with extern "C" directive to prevent name mangling that makes exposing it difficult on the JS module.
However, compiling this program gives error undefined symbol: _ZN4Task4testERi (referenced by top-level compiled C/C++ code), indicating that the "unmangled" int scratch() method cannot call the "mangled" int Task::test(int &c). Wrapping the class definition and implementation with extern "C" produces the same output.
How could I best call int Task::test(int &c) on an instance from a function that is marked extern "C"?
Mangling is close to a hack that was invented to allow the low level linker to make a difference between int foo(int) and int foo(void). You can use extern "C" to explicitely forbid mangling, but you declare symbols to have that linkage, so it should only be used for plain functions and not for classes nor class members.
The idiomatic way is to remember that a call to a non static member function carries a hidden this pointer. So you do not change anything to the Task class but declare a plain function to act as a relay:
extern "C" {
int task_test(Task *obj, int *c) { // C language does not know about reference hence a pointer
return obj->test(*c);
}
I have this code:
(exe)
#include <Windows.h>
#pragma comment(lib, "user32.lib")
class Dummy;
typedef void(Dummy::*Referece)(int i);
typedef void(*InitCall)(void*, Referece);
class Dummy
{
public:
Dummy(){}
void callMe(int val)
{
MessageBoxA(0, "ok", "ok", 0);
}
};
int main()
{
Dummy* obj = new Dummy();
HMODULE ha= LoadLibraryA("aa.dll");
InitCall val = (InitCall)GetProcAddress(ha, "Init");
val(obj, &Dummy::callMe);
}
and my dll:
(.h)
#pragma once
#define DLL_EXPORT __declspec(dllexport)
class Test;
typedef void (Test::*Reference)(int a);
#ifdef __cplusplus
extern "C"
{
#endif
void DLL_EXPORT Init(Test* Object, Reference reference);
#ifdef __cplusplus
}
#endif
(.cpp)
#include "your.h"
void DLL_EXPORT Init(Test * Object, Reference reference)
{
(Object->*reference)(1);
}
I reproduced the system and should be like this cause i can't change code in one side.
Why i get access violation? Calling "val(obj, ref)" i expect a pointer to class + offset to method call.
A pointer to member is not an "offset into the class." There's no such thing. In some cases (such as a pointer to a virtual member function in a class with a simple inheritance hierarchy), its implementation can consist of such an offset (plus potentially a few other bits of data).
However, for a non-virtual function (like in your example), it likely has a plain pointer to function underneath it. Non-virtual functions are not stored in any "table" with "offsets" (there is at least no reason to store them that way), they're most likely implemented as normal bog-standard functions with a mangled name and a prepended prameter.
Pointers to member are a somewhat tricky part of C++, largely due to the fact that there is no obvious mapping to an implementation concept and different compilers can handle them in different ways. Trusting that a void (Dummy::*)(int) and void (Test::*)(int) are binary compatible is quite fragile.
In general, you cannot expect the binary representation of a pointer to Dummy::callMe to be in any way similar to that of a pointer to a member function of Test, as it can depend too much on the definitions of Dummy and Test, and how the compiler implements pointers to member.
To top it off, the way in which Visual Studio's compiler handles pointers to member by default is non-conforming (so, from most perspectives, broken). This default handling is such that to correctly form a pointer to member of a class, the compiler needs to have seen the definition of the class. The reason is that the most general implementation of a pointer to member is quite large (4 native words, I believe), because it has to account for virtual inheritance and such. The most common case of a single-base class with no virtuals can fit in a native word.
Therefore, if you want to reliably use perfectly standard C++ constructs like accepting a pointer to member of a class whose definition is not visible at the site, you have to use the compilation flag /vmg. With this, the most general representation will always be used.
The default behaviour, /vmb, optimises the binary representation (including size!) of A::* based on the definition of A. It's therefore impossible to create a typedef such as yours with this behaviour in effect.
As to what your options are:
If you absolutely have to pass through a C-style interface, force use of a C-style function as a callback on the side calling the callback, and create a wrapper C-style function on the registering side. Something like this:
class Dummy
{
void callMe(int) {}
};
extern "C" void fw_Dummy_callMe(void *self, int i)
{ static_cast<Dummy*>(self)->callMe(i); }
Plus
#ifdef __cplusplus
extern "C"
{
#endif
void DLL_EXPORT Init(void* Object, void (*reference)(void*, int));
#ifdef __cplusplus
}
#endif
If you can have C++ in the interface (that is, the compiler and version will always be the same on both sides of the DLL interface), you can use a pointer to member function provided that:
The two sides will not see different definitions of the class. It's fine if one of them only has a non-defining declaration, though. To be 100% C++ conformant, the names of the class should be the same.
You use /vmg when building both the DLL and its client.
I have a C struct that is used in various C and C++ code (via extern "C").
#ifdef __cplusplus
extern "C" {
#endif
typedef struct A A;
struct A {
/*some members*/
};
#ifdef __cplusplus
}
#endif
Allocation, initialisation and release is done by separate member functions under my control, but I do not control access to members, as they are accessed all over the place.
The trouble is, I cannot change the struct's definition in the header that is heavily used throughout the system, but I still want to extend the type and add some members. Since this must compile as both C++ and C, I cannot simply create a derived type struct B : public A. So my idea was to add this type to the cpp file:
#ifdef __cplusplus
extern "C" {
#endif
typedef struct B B;
struct B {
A parent; // <---- public inheritance
/*some members*/
};
#ifdef __cplusplus
}
#endif
Now, I can modify all functions in the cpp file but I still have to hand out and accept A* outside of the compilation unit as nobody knows what B is.
So, I'm wondering if there is a sane and well-defined way of doing this. Can I simply cast my B*s to A*s and back or would it I have to explicitly convert them:
A* static_cast_A(B* b) {
return &(b->parent);
}
B* static_cast_B(A* a) {
B* const b = 0;
unsigned const ptrdiff = (unsigned)((void*)(&(b->parent)));
return (B*)(((void*)a)-ptrdiff);
}
Are there any more problems with this or should I do it differently altogether?
As long as you don't introduce a virtual table (by adding a virtual method or destructor) in struct B and parent member of that struct is the first element, then it is safe to just case B to A.
Casting A to B is safe, too, but only if the original pointer is actually pointing to B and not to something else, like only A structure itself, obviously.
In case when parent is not the first member of struct B, you would have to use container_of macro as seen in Linux kernel, for example. It works off the member pointer offsets (i.e. some good explanation can be found here).
Also, by default both structs will have the same alignment. I am not sure how the compiler would place A into B if you tweak one of the structs alignment. I guess that is compiler dependent, but should be easy to figure out.
If B has a virtual table, you must know compiler internals to convert the pointers correctly. For example, GCC on x86_64 adds 8 bytes offset thus you have to offset it manually when converting. But that won't work in case of virtual inheritance (virtual base classes), to solve that you have to resort to using RTTI etc... But this is outside of the scope of your question and I'd recommend you don't go that way.
Hope it helps.
Alignment requirements shouldn't cause any problems, and this is indeed the default way to fake inheritance in C.
However, your static_cast_B() is not portable (in particular void * arithmetics and conversion to unsigned in case of sizeof (void *) > sizeof (unsigned) - the latter should still work, but is something of a code smell).
I suggest using the following code instead:
#include <stddef.h>
B* static_cast_B(A* a) {
return (B*)((char*)a - offsetof(B, parent));
}