Frank Barknecht wrote:
Basically it seems to me that there are two ways to reuse C++ libraries/header/cpp files:
<now I'll start jabbering, as I don't fully understand the following ;) >
- Wrap it up with CPPExtern of GEM or something similar, maybe more sophisticated - and code in pure C++ yourself, with OO and all that bells and whistles.
Yes, you can always do this.
- Leave out the _real_ object orientation and write the main external code in C++, but not using a lot of the C++ features. I think, that's what Yves did in his stksitar adaption of Perry Cooks C++(!)-library STK. Reading through "stksitar~.cc" all this looks a lot like your typical C-external code, except usage of a sitar class which comes out of a C++-class defined in sitar.h (by P.Cook?!) Later on the setup function "stksitar_tilde_setup" is defined as: extern "C" void stksitar_tilde_setup(void) So obviously this function needs special treatment, whereas the others work without the extern declaration.
Perry Cook's toolkit is a good example of using an existing C++ library in C. In generaly, you don't have to leave out the "real object orientation" in this case, although maybe I'm not sure what you mean by that. Certainly if your extern is a C++ file compiled by a C++ compiler, you can use any C++ features you wish. (Yes, if sitar.h is part of the STK, then it was written by Perry Cook without regards for later being used in a C application). The problem is not one of not being able to use all the features of C++; rather it is more a design problem of how to interface classes written in C++, to PD's own object system which is implemented in C.
As someone else explained, what the "extern "C"" does is to arrange that the function will show up in the object file's symbol table with a name that is compatible with the symbol name produced by a C compiler. Without this, the C++ compiler will generate a name the encodes not only the function name, but the arguments and their types, and I think the return type as well. The reason for this, is that unlike in C, the C++ functions "void foo(int)" and "void foo(float)", for example, are actually two different functions. They might (as an example) be actually named in the symbol table as "v$foo$i" and "v$foo$f". (I just made that particular encoding scheme up, so you get the idea - it could really use any similar method.)
In the case of a PD extern, it is really only the setup function that is called *by-name* from C. The setup function in turn calls various PD functions to arrange the other functions to be called at appropriate times. I haven't looked at the PD STK code lately, but most likely the reason that the other functions, like the "new" function are not declared as "extern C", is because they are only called from PD through pointers (as arranged by the setup function). Hence in the case of the other functions, they are not called through two different compiled/linked modules (hence the linker is not involved), so that is why even though the "new" function (the pd-new and not "new" in the C++ sense) is in fact called through the PD code, there is no need for the "extern "C"" declaration.
Now, that is not the only consideration. Any function that is called from C code can not be a C++ class member function. That is because C++ member functions invisibly take a first argument which is a pointer to the class that they are being called for. And naturally, the existing C code will not pass the "this" pointer". This is the real design problem usually, when calling a C++ library from C code. Talking in general terms now, and not about PD, a typical idiom is something like this:
extern "C" void CFooBar ( <args> ) { Foo *foo = FindObjectToCall ( <args> ); foo->Bar ( <args> ); }
or class Foo { public: static Foo* FindObjectToCall ( <args> ) void Bar( <args> ) };
extern "C" void CFooBar ( <args> ) { Foo *foo = Foo::FindObjectToCall ( <args> ); foo->Bar ( <args> ); }
Another issue with using C++ code with PD, is that of memory allocation. PD has defined its own allocation routines (what is it, "pd_malloc" or something like that?). So if you allocate your C++ objects using the "new" operator, you won't pass through the PD-specific memory allocation. My guess is that this is not a problem, however. I think that the PD allocation routines simply use the C library's "malloc", in which case there would be no conflict.
Well, I'm gonna have to stop now before writing a whole book. Hope this helps!
Larry Troxler
Why does 2) work? Can I just code right away in C++, and then just declare the _setup function as 'extern "C"' and all is well? I don't think so, but where is this the right, or the convenient way to go?