Reputation: 93
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?
Upvotes: 1
Views: 3416
Reputation: 308206
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.
Upvotes: 0
Reputation: 30840
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.
Upvotes: 1
Reputation: 85361
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.
Upvotes: 3
Reputation: 238351
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.
Upvotes: 1