vkchavda
vkchavda

Reputation: 93

Extern "C" and C++ structure and typedef

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

Answers (4)

Mark Ransom
Mark Ransom

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

Botje
Botje

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

rustyx
rustyx

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

eerorika
eerorika

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

Related Questions