C++ std:: constructs in static library used by C program

Started by
12 comments, last by Koshmaar 18 years, 8 months ago
Hi I've written C++ static library that uses std:string, std::iostream and std::map. Now I'd like to know, whether this lib can be used by programs written in C? Obviously, my question would be dead stupid if there would be anything in public interface, that couldn't be compiled in C. But there isn't, if you would take any .h file and compile it with C compiler, it should be compiled with no problems - there are only plain functions. However, those functions internally use all the std:: stuff I listed in first paragraph. I'm not sure - can such combination work without errors? If yes, then it would save me whole load of troubles with implementing them by myself... [Edited by - Koshmaar on August 12, 2005 5:32:11 PM]
Advertisement
Just make sure to wrap the function declarations you export in extern "C" when you compile it with the C++, and avoid C++ constructs in the function prototypes and it should be fine.
Quote:
Just make sure to wrap the function declarations you export in extern "C" when you compile it with the C++,


It's done already...

Quote:
and avoid C++ constructs in the function prototypes


... and it isn't, but it's just an issue of changing const & std::string to const char *, which is trivial.

Quote:
and it should be fine.


Grrrreat, thx for quick answer :-) Shame I can't rate you anymore (max already).

Hmmm, one more question.


In my C++ library I have this code in public header file:

typedef struct CFG_Internal_File; // (*)typedef struct CFG_File {   CFG_Internal_File * internal_file;  // (**) };


Since CFG_Internal_File contains std::map, its body can't be put directly into that header, it should be stored inside other, private header.
Reason for that is this: public header file will be included in all programs that will be using that lib, and those programs may be written in C or C++. For C++ std::maps are ok, but for C not.

Also, in C++ line (*) is perfectly valid, while in C I get "[Warning] useless keyword or type name in empty declaration " and (**) generates error.


So, my question is, is there a way to forward that declaration in a way, that will be understanable for C compiler? Btw, it doesn't have to be supported both by C/C++, since I can add #ifdef __cpluscplus and problem's over.


Don't know, maybe it's a silly question, but you know, I have almost 4 years of experience in C++ and almost 1 day in C... [embarrass].
The standard way of doing C++ to C conversions is to wrap everything in C, including allocations and deallocations without leaking out type information. Rather than having an 'object' you have a 'handle to an object' which is just a void pointer which can be cast to the appropriate type.

In this case I'd be looking for another 'layer of abstraction' through a wrapper class - but it's hard to say without seeing a little more code whether what you have already will suffice.

class YourClass{    ...};extern "C" {typedef TypeHandle void*;TypeHandle* createType(){    return new YourClass;}void releaseType(TypeHandle* instance){   YourClass* pClass = (YourClass*)instance;   delete pClass;}void operation1(TypeHandle* instance, [other_arguments] ){   YourClass* pClass = (YourClass*)instance;   pClass->operation1( [other_arguments] )}void operation2(TypeHandle* instance, [other_arguments] ){   YourClass* pClass = (YourClass*)instance;   pClass->operation2( [other_arguments] )}}


Edit: fixed source tags
Quote:Original post by Koshmaar
... is there a way to forward that declaration in a way, that will be understanable for C compiler?


A major role of structure definitions is to provide the compiler a sense of the memory requirements of a structure. Since the internal file member of the structure is a pointer, and pointer sizes are independent of whatever they point to (ie on a 32 bit system a pointer requires 4 bytes regardless of whether it points to 4 bytes or 4 megabytes), there shouldn't be a problem.

At any rate, you should be able to leave the forward definition of CFG_Internal_File alone, provided you include the struct keyword in the definition of CFG_File. Like so:

typedef struct CFG_Internal_File;typedef struct CFG_File {   struct CFG_Internal_File * internal_file;  // (**) };


Note that any C code that references these types will always have to include the struct keyword as the point of declaration. For example,

struct CFG_File cfgfile;

Here's one approach that would allow for declaring an instance of the type without having to use the struct keyword.

typedef struct tagCFG_Internal_File CFG_Internal_File;typedef struct tagCFG_File {   CFG_Internal_File * internal_file;  // (**) }CFG_File;


And then

CFG_File cfgfile;

"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Thanks for replies guys, but unfortunately they didn't help.

XXX_Andrew_XXX: I think you've totally missunderstood my problem :-) I don't want to do any C++ to C classess conversions. Maybe I'll explain it once more,

Here's more code from public header file (SDL_Config.h):

typedef struct CFG_Internal_File; // (*)typedef struct CFG_File {   CFG_Internal_File * internal_file; // (**) };extern int CFG_OpenFile( CFG_String_Arg filename, CFG_File * file ); // (***)// ... rest of functions that use CFG_File in their declarations


So, in summary, I'll say it once again: I have static library that is compiled in C++, with no problems. That library can be used from within programs compiled with C++, also - without any problems.

However, there are problems when that lib is used by C program, reason: unstasified forward declaration. Public interface of this library is 99% compatible with good old C, and the only thing that creates problems is the fact, that definition of CFG_Internal_File must stay inside .c file, because - it contains std::map as member. And when C compiler sees std::map... you know what happens :-) "Error! Error! Error!" etc.

I want to forward declare CFG_Internal_File so that it can be referenced from public header file, but its implementation should stay in internal .c file.

Currently above library code generates those errors in test programs, when they are compiled in C, with DevCpp/MinGW:

 (*) [Warning] useless keyword or type name in empty declaration  (**) parse error before "CFG_Internal_File"  (**) [Warning] no semicolon at end of struct or union  (***) error: parse error before "CFG_File"



LessBread: you were much closer, unfortunately this code:

typedef struct CFG_Internal_File; // (*)typedef struct CFG_File {   struct CFG_Internal_File * internal_file; // (**) };extern int CFG_OpenFile( CFG_String_Arg filename, CFG_File * file ); // (***)



still generates this set of errors:

 (*) [Warning] useless keyword or type name in empty declaration  (**) [Warning] useless keyword or type name in empty declaration  (***) error: parse error before "CFG_File"



If I have included not enough source code for you to correctly adress this problem, here's link to CVS via web browser: clicky. You should be interested in SDL_Config.h.
Quote:Original post by Koshmaar
LessBread: you were much closer, unfortunately this code: still generates this set of errors:


Try this

typedef struct CFG_Internal_File CFG_Internal_File;typedef struct CFG_File {   CFG_Internal_File * internal_file;} CFG_File;


Actually, you might even be able to get away without needing the forward declaration at all.

typedef struct CFG_File {   struct CFG_Internal_File * internal_file;} CFG_File;


So long as the member is a pointer to a structure and no members of the structure are referenced in the C code, the compiler should have everything it needs.

And this error

(**) [Warning] no semicolon at end of struct or union

suggests rechecking the code for missing semicolons. ... Yeah I know that's not fun. Hopefully the warning is merely a byproduct of the forward reference issue.


"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Quote:Original post by LessBread
Actually, you might even be able to get away without needing the forward declaration at all.

typedef struct CFG_File {   struct CFG_Internal_File * internal_file;} CFG_File;



You were right - it worked both in C and C++. I knew that when you're using pointers to structs, they don't have to be explicitely defined, but forgot to try this out. Uhhhm, that was reaally stupid problem :-/ but ++ratings for you.

...

Nooo, and here come's anoher stupid problem: I've compiled that lib in C++ and compiled program that was using it, in C. So far so good. Unfortunately, during linking I got thousands of errors about 'undefined reference to C++ constructs (std::string, new, delete, map etc.)', ie:

(SDL_config.o.b)(.text+0x3cc):SDL_config.c: undefined reference to `__gxx_personality_sj0'(SDL_config.o.b)(.text+0x459):SDL_config.c: undefined reference to `operator new(unsigned)'(SDL_config.o.b)(.text+0x49c):SDL_config.c: undefined reference to `operator delete(void*)'(SDL_config.o.b)(.text+0x516):SDL_config.c: undefined reference to `std::string::c_str() const'(SDL_config.o.b)(.text+0x6ca):SDL_config.c: undefined reference to `std::string::operator=(char const*)'(SDL_config.o.b)(.text+0x6f9):SDL_config.c: undefined reference to `std::string::operator=(char const*)'(SDL_config.o.b)(.text+0xaa8):SDL_config.c: undefined reference to `__gxx_personality_sj0'


It's strange, since C++ stuff should already be linked in C++ library, not in C program which is using lib. However, that's probably another newbish problem caused by my lack of experience in C :-/ Does anyone know what to do in this case? What project options should I change in library / program so that it'll be linked properly? I'm using DevCpp / MinGW.
Quote:Original post by Koshmaar
Nooo, and here come's anoher stupid problem: I've compiled that lib in C++ and compiled program that was using it, in C. So far so good. Unfortunately, during linking I got thousands of errors about 'undefined reference to C++ constructs (std::string, new, delete, map etc.)', ie:

(SDL_config.o.b)(.text+0x3cc):SDL_config.c: undefined reference to `__gxx_personality_sj0'(SDL_config.o.b)(.text+0x459):SDL_config.c: undefined reference to `operator new(unsigned)'(SDL_config.o.b)(.text+0x49c):SDL_config.c: undefined reference to `operator delete(void*)'(SDL_config.o.b)(.text+0x516):SDL_config.c: undefined reference to `std::string::c_str() const'(SDL_config.o.b)(.text+0x6ca):SDL_config.c: undefined reference to `std::string::operator=(char const*)'(SDL_config.o.b)(.text+0x6f9):SDL_config.c: undefined reference to `std::string::operator=(char const*)'(SDL_config.o.b)(.text+0xaa8):SDL_config.c: undefined reference to `__gxx_personality_sj0'


It's strange, since C++ stuff should already be linked in C++ library, not in C program which is using lib. However, that's probably another newbish problem caused by my lack of experience in C :-/ Does anyone know what to do in this case? What project options should I change in library / program so that it'll be linked properly? I'm using DevCpp / MinGW.


Your going to have a hell of a time [grin].

All standard library containers (including std::string and the string streams) are parameterized by allocator type with a default std::allocator which uses operator new/delete (for allocation/deallocation only) & placement new operator (for construction only), you will need to use a different allocator type, as you are using a GCC compiler already you can just use __gnu_cxx::malloc_allocator in header ext/malloc_allocator.h as you can probably tell its uses malloc/free for allocation/deallocation instead but remember its not part of the standard its a GCC library extension so its trivial to define your own if you want.

An e.g.:

#include <cstdlib>#include <ext/malloc_allocator.h>#include <string>typedef std::basic_string<char, std::char_traits<char>, __gnu_cxx::malloc_allocator<char> > my_string_type;


[Edited by - snk_kid on August 13, 2005 5:35:34 PM]

This topic is closed to new replies.

Advertisement