Cpp2C: An extern C wrapper code generator for C++

A Simple Example

Suppose you have the following header file, at C:\Projects\Test\eTst.hpp

 

(Input:) Test.Hpp

Click class B {
    public:
    B();
    virtual int vir_func()=0;
    static int stat_func();
    virtual ~B();
};

class D: public B {
    public:
    int vir_func();
    void func(int mandatory_arg, int first_opt_arg=2, int second_opt_arg=3);
};


namespace Test {
    int namespaced_func(const D& arg);
};


B* factory(int type);

bool predicate(D elems[]);

Usage

To generate the wrapper code using msvc9 under Windows, you type

python cpp2c.py "C:\Projects\Test\Test.hpp" -g "C:\Program Files\gccxml 0.9\bin" -t msvc9

This generate the output file (along with the corresponding h file and def file), with .the name of the input file, followed by the _C_Wrapper suffix.

(Output:) test_C_Wrapper.cpp

// (1)

#include "Test.hpp"

#include "Test_C_Wrapper.h"


#ifdef WIN32
#include <Windows.h>
extern "C" BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}
#endif  // WIN32

 

// (2)
PTR_B B_B(BOOL_C *ptr_was_exception, const PTR_B arg0) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_B)new ::B(*(const ::B*)arg0);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_B) NULL;

    }

}

PTR_B B_B2(BOOL_C *ptr_was_exception) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_B)new ::B;
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_B) NULL;

    }

}

// (3)
void B_delete_B(BOOL_C *ptr_was_exception, PTR_B class_this) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        delete ((::B*)class_this);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

// (4)
PTR_B B_B_array(BOOL_C *ptr_was_exception, size_t arr_size) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_B)new ::B[arr_size];
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_B) NULL;

    }

}

void B_delete_B_array(BOOL_C *ptr_was_exception, PTR_B class_this) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        delete []((::B*)class_this);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

// (5)
int B_stat_func_static(BOOL_C *ptr_was_exception) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return ::B::stat_func();
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (int) NULL;

    }

}


// (6)

PTR_B B_operator_assign(BOOL_C *ptr_was_exception, PTR_B class_this, const PTR_B arg0) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_B)new ::B(((::B*) class_this)->::B::operator=(*(const ::B*)arg0));
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_B) NULL;

    }

}

PTR_D D_D(BOOL_C *ptr_was_exception, const PTR_D arg0) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_D)new ::D(*(const ::D*)arg0);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_D) NULL;

    }

}

PTR_D D_D2(BOOL_C *ptr_was_exception) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_D)new ::D;
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_D) NULL;

    }

}

void D_delete_D(BOOL_C *ptr_was_exception, PTR_D class_this) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        delete ((::D*)class_this);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

PTR_D D_D_array(BOOL_C *ptr_was_exception, size_t arr_size) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_D)new ::D[arr_size];
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_D) NULL;

    }

}

void D_delete_D_array(BOOL_C *ptr_was_exception, PTR_D class_this) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        delete []((::D*)class_this);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}


// (7)

int D_vir_func(BOOL_C *ptr_was_exception, PTR_D class_this) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return ((::D*) class_this)->::D::vir_func();
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (int) NULL;

    }

}


// (8)

void D_func(BOOL_C *ptr_was_exception, PTR_D class_this, int mandatory_arg, int first_opt_arg, int second_opt_arg) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        ((::D*) class_this)->::D::func(mandatory_arg, first_opt_arg, second_opt_arg);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

void D_func2(BOOL_C *ptr_was_exception, PTR_D class_this, int mandatory_arg, int first_opt_arg) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        ((::D*) class_this)->::D::func(mandatory_arg, first_opt_arg);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

void D_func3(BOOL_C *ptr_was_exception, PTR_D class_this, int mandatory_arg) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        ((::D*) class_this)->::D::func(mandatory_arg);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
    
    }

}

PTR_D D_operator_assign(BOOL_C *ptr_was_exception, PTR_D class_this, const PTR_D arg0) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_D)new ::D(((::D*) class_this)->::D::operator=(*(const ::D*)arg0));
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_D) NULL;

    }

}


// (9)

BOOL_C predicate(BOOL_C *ptr_was_exception, PTR_D elems) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (BOOL_C)::predicate((::D*)elems);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (BOOL_C) NULL;

    }

}

// (10)
int Test_namespaced_func(BOOL_C *ptr_was_exception, const PTR_D arg) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return ::Test::namespaced_func(*(const ::D*)arg);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (int) NULL;

    }

}

// (11)
PTR_B factory(BOOL_C *ptr_was_exception, int type) {

    try {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = FALSE_C;
        return (PTR_B)::factory(type);
    }
    catch(...) {
        if((void *)ptr_was_exception != NULL) (*ptr_was_exception) = TRUE_C;
        return (PTR_B) NULL;

    }

}

Output Explanation and relevant command-line flags

There are several code segments in the output code:

  1. The prefix code is generated. Both the original (for included headers, etc.) and the auto-generated (for new typdefs and function declerations) are included.A DllMain() for Windows (and the .def file) is auto-generated if the --dl flag is no given in the command line.
  2. The constructors of class B are translated into C functions. Notice that the copy constructor is also (auto-)generated - since we didn't declare it as private. Notice that objects can only be created on the heap, and not on the stack.
  3. The destructor of class B is implemented as a function.
  4. The array versions of the empty constructor and destructors of class B are generated.
  5. The static function wrapper for class B is generated. Notice the _static suffix at the function name to identify static functions.
  6. The assignment operator of class B is auto-generated (with a name that fits a C function), since we didn't use the --operator flag to prevent operators from being created.
  7. The virtual function of class D is auto-generated. Notice that it wasn't created for class B - since it was pure virtual there.
  8. The optional arguments are being implemented by creating multiple variations of the same method, each one with a different number of arguments.
  9. The bool type is replaced by the BOOL_C typedef (defined at: test_C_Wrapper.h), unless the --c99 flag is being used - for compiler C99 support.
  10. The namespaced function is given the namespace name as its prefix, to prevent name collision
  11. A class is returned as an opaque pointer, defined at test_C_Wrapper.h, for a minimal level of type safety.

Besides the specific segments, each function has its own exception masking wrapper code - and the exception existence is signaled by the ptr_was_exception auto-generated output variable - unless you use the --error flag.

The generation of the exception handling code itself can be suppressed using the --nothrow flag.

 This source code can now be compiled as a static or dynamic library - and be called from a C code, using the extern C interface to access the C++implemented functionality.

The auto-generated header file, Test_C_Wrapper.h is a pure-C header file (with #ifdef __cplusplus, etc.), and can be included in the calling code C or C++ files.

Recent Forum Posts

No recent posts

Recent Blog Entries

No recent entries