[PD] Using C++ sources for externals

Larry Troxler lt at westnet.com
Thu Feb 7 03:03:10 CET 2002


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 ;) >
> 
> 1) 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.

> 2) 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?
>



More information about the Pd-list mailing list