AnsweredAssumed Answered

VRF - C to C++ DLL conversion problems

Question asked by VRFuser on Nov 4, 1996
from: Greg Goebel / HP-MXD / 970-679-3030 / FAX 970-679-5971
to:   Jim Kneale
date: Tuesday, 05 November 1996 1415 MST

> We were happily running some DLLs written in Borland C/C++ version 4.5.  We
> ran into some size limitations of malloc and free, and wanted to convert
> over to C++ to use more powerful functions (new and delete). 
> We re-compiled as a C++ program, and now VEE 3.12 comes back with an error
> 604 when we try to execute the functions.  The only changes were the memory
> allocation and the ".CPP" extension in the source file.  Do you have any
> suggestions?
> Jim Kneale     
> ph  (410)765-3807   ///  Northrop Grumman ESSD
> fax (410)993-8126  (..)  Baltimore MD USA
> ----------------ooO-()-Ooo------------------
> #include "std_disclaimer.h" //opinions are my own


We have some notes on this topic ... they follow below:

--------------------------------- cut here ----------------------------------


* The previous sections have focused on compiling DLLs in C++, but we have
also had questions on users wishing to compile in C++ -- in specific, on how
to call C++ class member functions directly from a VEE Call Function object,
and on using the the C++ "new" operator within the compiled functions.  This
section addresses these questions.

* C++ is an "object-oriented" variant of C, in which combinations of data
structures and functions are "encapsulated" in objects that are "instances"
of a defined "class".  In order to achieve encapsulating "member" functions
in an argument, C++ "mangles" the names of functions that appear in class

This mangling is necessary because different objects may have member
functions that have the same name.  Consider what would happen if you had two
classes that both had a member function named "foo".  Without some way to
reference each of the functions to the appropriate class, there would be no
way for the C++ compiler to figure out which "foo" to use for a call like
"".  To get around this problem, C++ "decorates" each function
definition by prepending and appending information appropriately; there is no
standard way of doing this, however, and different C++ compilers may do it

This causes a severe problem with the VEE "Call Function" object, since it
needs to know the exact appearance of the function name in order to access
it.  Because of C++'s need to "mangle" function names, the "Call Function"
box cannot find a reference to "foo".

* A second problem that comes up when you try to directly call C++ class
member functions is that the only time you know for certain that an an
instance of a class has been properly constructed is when you are calling a
member function from a C++ application.  For example, if you have a function

   myClass anInstance;

-- in your C++ program, the C++ compiler will, at compile time, put all the
necessary mechanics to call "myClass's" "constructor" function when the
variable "anInstance" comes into scope.  The compiler will also encode the
necessary mechanics to call "myClass's" "destructor" when the variable
"anInstance" goes out of scope.

VEE is a C application, so if the Call Function box were to call into a class
member function, it is very possible that the class instance would not have
been properly constructed -- leading to "unpredictable" results.

* The solution to these problems is to "wrap" your C++ code in C functions
that VEE's Call Function object can find in your DLL or shared library.  C++
allows you to turn off name mangling for functions that are not associated
with a class -- through a language linkage specification that you provide
when you declare your function.

Let's say you have developed a class to manipulate waveforms.  The most
trivial class implementation for a waveform would have a variable
representing the numbers that make up an array along with a number that tells
you how many points are in the array.  You might also have a few methods and
operators to manage and manipulate a waveform.

The way to access that class from a VEE compiled function would be to have
one or more additional C functions that contain references to your waveform
class.  It might look something like this:

   extern "C" long addValue( double *veeArray, long arraySize,      
   double valueToAdd )
     waveForm *trace;

     if( arraySize < 1 )
       return( 0 );
     trace = new waveForm( veeArray, arraySize );
     *trace += valueToAdd;
     delete trace;
     return( arraySize );

The 'extern"C"' tells the C++ compiler to link this function using C-language
naming conventions, preventing the name mangling.  Notice that the function
declared a variable that is of type "waveForm", which is an instance of your
C++ class; the function can then call all the member functions operators of
class "waveForm" with confidence that the class instance is properly
constructed and destroyed.

* One consideration when you are building C++ compiled functions on HP-UX is
that, because VEE is a C application, it doesn't link in the C++ runtime

Runtime libraries are part of the operating system and provide those services
essential to a process running under that OS.  For example, if a C program
calls "printf()", the library that contains the implementation of "printf()"
is the C runtime library.

If -- as in the case of the example above, where we use the C++ operators
"new" and "delete" -- your C++ compiled function uses functions from the C++
runtime libraries, you need to link your compiled functions against the
libraries "libC" and "libcxx".  The HP-UX makefile presented later shows you
how to do this.

* For an example, consider a C++ compiled function that implements a trivial
waveform class.  It has one operator that lets you add a value to all the
points in the waveform.  It includes the following source files:

A:  usingNew.cpp:

  #include "usingNew.h"

  waveForm::waveform( double *veeArray, long arraySize)
    waveData = veeArray;
    nElements = arraySize;

  void waveForm::operator+=( double aValue )
    for( long i = 0; i < nElements; i++ )
      waveData[i] += aValue;

  long addValue( double *veeArray, long arraySize, double valueToAdd )
    waveForm *trace;
    if( arraySize < 1 )
      return( 0 );
    trace = new waveForm( veeArray, arraySize );
    *trace += valueToAdd;
    delete trace;
    return( arraySize );

B:  usingNew.h:

   #if !defined( _waveFormClass_ )
   #define _waveFormClass_

   class waveForm
       double *waveData;
       long nElements;
       waveForm( double *, long );
       void operator+=(double);

   extern "C" long addValue( double veeArray, long arraySize,
   double valuetoAdd );


C:  usingNew.vee -- this is the VEE program, and it has the form:

            | Import  |
            | Library |
   |          Formula          |
   +---------------------------+      +---------+
   | sin( ramp( 720, 0, 719 )) +--+-->| totSize +--+
   +---------------------------+  |   +---------+  |
                                  |                |
   +------ source array ----------+                |
   |                                               |
   |  +-------------- array size ------------------+
   |  |
   |  |           +------------------------------------------+
   |  |           |            Call myLib.addValue           |
   |  |           +------------+------------------+----------+
   +--|---------->| veeArray   |  Function Name   | RetValue +-----+
   |  +---------->| arraySize  |                  |          |     |
   |          +-->| valueToAdd |[ myLib.addValue ]| veeArray +--+  |
   | +-----+  |   +------------+---------+--------+----------+  |  |
   | | 1.1 +--+                          |                      |  |
   | +-----+                +------------+-----------+          |  |
   |                        |     Delete Library     |          |  |
   |                        +------------------------+          |  |
   |                        | Library Name [ myLib ] |          |  |
   |                        +------------------------+          |  |
   |                                                            |  |
   |  +--------------------- RetValue --------------------------+  |
   |  |                                                            |
   |  |  +------------------------- veeArray ----------------------+
   |  |  |
   |  |  |                      +---------------------------------+
   |  |  |                      |             XY Trace            |
   |  |  |                      +---------+-----------------------+
   |  |  |                      |         |                       |
   +--|--|--- source array ---->| Trace 1 |                       |
      |  |                      |         |                       |
      |  |   +--------------+   |         |                       |
      |  |   |   Formula    |   |         |                       |
      |  |   +---+----------+   |         |                       |
      |  +-->| A | B[0:a-1] +-->| Trace 2 |                       |
      +----->| B |          |   |         |                       |
             +---+----------+   |         |                       |
                                |         +-----------------------+
                            +-->| AutoScale                       |
                            |   |                                 |
                            |   +----------------+----------------+
                            |                    |

D:  usingNew.vh -- This is the file you provide to VEE's Import Library
object; its contents allow VEE to mesh the functions it is to call:

   long addValue( double *veeArray, long arraySize, double valueToAdd );

E:  usingNew.def -- for WIN95 and WIN/NT only; allows the linker to make
specified functions visible to modules external to the one that implements
the function.  In our case, any function we want VEE to be able to call must
be listed in the "EXPORTS" section:


F: -- for WIN95 and WIN/NT only; a makefile written for
Microsoft Visual C++:

   CFLAGS = -c -G3 -Od -W3 -Zi -D_X86_=1 -DWIN32 -nologo
   LFLAGS = -debug:mapped,full -debugtype:cv -pdb:none -nologo
   LIBS   = libc.lib kernel32.lib user32.lib

   all: usingNew.dll

   usingNew.dll: usingNew.obj

             cl $(CFLAGS) $*.cpp
             rc $*.rc
             cvtres -i386 $*.res -o $*.rbj

             link $(LFLAGS) -DLL   -subsystem:windows -out:$*.dll
                  -def:$*.def $*.obj $(LIBS)

G: -- A makefile for HP-UX; note that we have linked against the
C++ runtime libraries by specifying "-lcxx" and "-lC".  These link commands
must appear in this order.  Notice also that we linked by using "CC" and not
"ld"; this is necessary to allow HP-UX's dynamic loader (the system mechanism
that allows shared libraries to operate) to call the constructor and
destructor functions for instances of C++ classes that are declared as
"static" in the C++ implementation file:

  # $Header: makefile, v 1.0, 95/10/11 17:48:19 jd Exp $
  # $Date: 95/10/11 17:48:19 $
  # $Revision: 1.0 $


          co usingNew.C
          co usingNew.h
          co usingNew.vee

  usingNew.o: usingNew.C usingNew.h usingNew.vee
          CC -c -g +z usingNew.C usingNew.o
          CC -b -o usingNew.o -lcxx -lC
          chmod 555

  # $Log:  makefile,v $
  # Initial revision