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".
Related
Let's say I wanted to forward declare CLibraryStruct in my MyClass.hpp
class CLibraryStruct;
class MyClass {
private:
CLibraryStruct *cLibStr;
};
At the same time I also wanted to be compliant with MISRA C++ 2008 Rule 7–3–1:
The global namespace shall only contain main, namespace declarations and extern "C" declarations.
I obviously can't use anonymous namespaces here, but I think I could try this:
class MyClass {
private:
class CLibraryStruct *cLibStr;
};
Or this:
#include "CLibrary.h"
class MyClass {
private:
CLibraryStruct *cLibStr;
};
Would you be so kind as to help me out and explain to me which would be a better choice (or suggest an alternative)?
I have started using C++ for win32. As we know C++ struct is same as class but default public member etc... Now I want is simple C struct that has no default constructor or copy or move operation or any other magic. Because I want to store it in file also perform memcpy, use as BYTE array etc... So I thought of defining it in header with #ifdef __cplusplus as follow.
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PersonTag
{
int ID;
char Name[200];
} Person, *PPerson;
#ifdef __cplusplus
}
#endif
But this only prevent name mangling for function. But struct still compile as cpp struct if in cpp file or as C struct if in C file. I tested it by including header in cpp file and in C file. Both compiles well but if I add constructor in struct body as following.
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PersonTag
{
PersonTag();
int ID;
char Name[200];
} Person, *PPerson;
#ifdef __cplusplus
}
#endif
C++ compile it without err but C fails as it should be. Does that means that even if I include struct definition inside #ifdef __cplusplus it will still compile as C++ struct with all copy and move magic with it ?
I was thinking that by defining struct in side ' extern "C" ' will also produce err in cpp file if struct has c++ style constructor etc...
So my question is, Is there a way to tell c++ compiler(vc++) to consider and compile it as like it is pure C struct, no default constructor, no default destructor, move or copy like thing or anything else.
My second question is about typdefing cpp function pointer and passing this function pointer to C function which may call it back.
I am using C lib (sqlite3) and passing callback fun. Now if I typedef function pointer in cpp file by following way.
typedef int (*SQL_CALLBACK_FUN)(void* NotUsed, int argc, char** argv, char** azColName);
in Cpp file the function might be like
int Callback_GetAllPerson(void* NotUsed, int argc, char** argv, char** azColName);
and than the function pointer be
SQL_CALLBACK_FUN sql_callback_fun = Callback_GetAllPerson;
Now I am going to pass this "sql_callback_fun" function pointer to C lib function (sqlite3_exec).
My question is, here I typdef function pointer in CPP also the callback function will compile as cpp. now I am passing it to C lib as pointer and the C lib function will call back this cpp function via pointer.
Is there any runtime err I might face? or the function pointer (typdef) is same for both cpp and c. regardless of name mangling things?
But struct still compile as cpp struct if in cpp file or as C struct if in C file
You can't compile some code as C using a C++ compiler. A C++ compiler compiles everything as C++ code.
Fortunately the C++ language guarantees that "standard-layout" classes are layout-compatible with C. So you have nothing to worry about, your struct will be identical when compiled as C or C++. The extern "C" only affects name mangling (adds a leading underscope, for example), so that identifiers compiled as C++ can be "found" by C code during linking.
Obviously you should not add C++ features like a constructor, if the struct is to be compiled as C as well as C++.
For free function pointers, make sure to use extern "C" to enforce C language linkage.
So your very first code fragment looks fine.
But this only prevent name mangling for function. But struct still compile as cpp struct if in cpp file or as C struct if in C file.
This is actually a good thing. This allows you to use the struct in both languages.
I was thinking that by defining struct in side ' extern "C" ' will also produce err in cpp file if struct has c++ style constructor etc
You were thinking wrong. It only affects name mangling.
Is there a way to tell c++ compiler(vc++) to consider and compile it as like it is pure C struct
No. C++ compiler compiles C++; C compiler compiles C.
If you want a struct definition to be compatible in both languages, then you must write it in common subset of both languages.
Is there any runtime err I might face [with callbacks]?
Typical potential problem is that if the callback throws, then things blow up because the calling C code won't deal with exceptions. You must make sure that the callback doesn't throw.
extern "C" has nothing to do with the struct. But you are on the right track though, you just need to #ifdef the constructor:
/* Compiles as both C++ and C */
struct Person {
int ID;
char Name[200];
#ifdef __cplusplus
Person() : ID(0), Name("") {}
#endif
};
typedef struct Person Person, *PPerson;
If you really don't want to have access to the constructors generated by the compiler, you can delete them:
#ifdef __cplusplus
Person() = delete;
Person(const Person&) = delete;
Person(Person&&) = delete;
#endif
Compiling the following code to test proves that these structures have identical sizes:
int main () {
printf("Person is %d bytes\n", sizeof(Person));
}
output:
$ g++ -o foo_cpp foo.c
$ ./foo_cpp
Person is 204 bytes
$ gcc -o foo_c foo.c
$ ./foo_c
Person is 204 bytes
As for your second question: yes, passing a C++ function pointer will work as expected. Symbol mangling and conversion to an address is handled by the C++ compiler.
For a simple struct with no class members, the default constructor and destructor will be empty functions. No members will be initialized, and no members will be destroyed. A good optimizer will completely eliminate the calls to those empty functions, leaving you with the same generated code under both C and C++.
Similarly with the default copy constructor and copy operator, they will do a simple bit-wise copy of the entire struct. Same operation regardless of whether it's C or C++.
Since C can't do move operations, no need to even think about it.
In short, there's nothing to worry about. Consider that Microsoft defines the entire Windows API with C structures, but people call it from C++ every day.
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++ 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.
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));
}