Amir-Mousavi
Amir-Mousavi

Reputation: 4561

How to write Wrapper for accessing C++ class member from C (with inheritance and constructor)

In most of the questions, I see the wrapper for a simple class without a constructor, inheritance and just calling a void* for creating and destroying and a foo function.

For a structure like below how should create a wrapper to access the class member from C Code.

myHeader.h for c++
-------------------
class childA:public parentA {private: void logger() override}

class childB:public parentB 
{ 
  private: /*some members*/ 
  protected: /*some members*/
  public: 
      explicit childB(childA* a);
}
class parentB
{
  protected:
      MyType object;
  public:
      boolean Init(MyType obj); /*the implmentation is object=obj*/
}

Now in a C code, I want to access the object. How should I write the wrapper for this?

Object type is a function pointer => typedef S32(*IoFunc)(Msg&);
where S32 is unsigned int, Msg is a struct.

Thanks for your help.

Upvotes: 4

Views: 1242

Answers (2)

Robert Andrzejuk
Robert Andrzejuk

Reputation: 5222

What you want is to "un-objectify" the functions.

Every public function that is inside a class has to be created outside with the first parameter void* and the rest of the parameters the same as in the member function.

This means you also have to create a "constructor" and a "destructor" function for the object.

There are 2 ways these functions can work, depending or where the data is to be stored: memory provided by the caller or memory allocated by the library (on the "free store").

The first thing that the new functions need is to declare linkage https://en.cppreference.com/w/cpp/language/language_linkage

Only two language linkages are guaranteed to be supported:

  1. "C++", the default language linkage.
  2. "C", which makes it possible to link with functions written in the C programming language, and to define, in a C++ program, functions that can be called from the modules written in C.

So in the new header when it is used in C++ the linkage has to be declared, but when the header is used in C the linkage has to be removed:

So this is needed at the beginning:

#ifdef __cplusplus
extern "C"
{
#endif

and at the end:

#ifdef __cplusplus
}
#endif

Memory allocated on the "free store".

The declaration of the functions in the header within the above code should be:

void* childA_construct(); // ChildA doesn't have and constructor paramters
void* childA_destruct();

void* childB_construct(void* ptr_childA);
void* childB_destruct();

void* parentB_construct(); // parentB doesn't have and constructor paramters
void* parentB_destruct();
bool  parentB_Init(struct MyType m);

Next in the implementation file:

extern "C"
{
    void* childA_construct()
    {
        return static_cast< void* >(new childA());
    }
    void  childA_destruct(void* ptr_childA)
    {
        delete static_cast< childA* >(ptr_childA);
    }

    void* childB_construct(void* ptr_childA)
    {
        childA* a_ptr = static_cast< childA* >(ptr_childA);
        return static_cast< void* >(new childB(a_ptr));
    }
    void childB_destruct(void* ptr_childB)
    {
        delete static_cast< childB* >(ptr_childB);
    }

    void* parentB_construct()
    {
        return static_cast< void* >(new parentB());
    }
    void* parentB_destruct(void* ptr_parentB)
    {
        delete static_cast< parentB* >(ptr_parentB);
    }

    bool  parentB_Init(void* ptr_parentB, struct MyType mt)
    {
        parentB* ptr_pb = static_cast< parentB* >(ptr_parentB);
        return ptr_pb->Init(mt);
    }
}

Memory allocated by caller

If the interface requires that the caller allocates memory, then the caller needs to know how much memory to allocate, so one way is to make a function return the required size.

Then in the construct method "placement new" has to be used to call the constructor.

While in the destruct function, the destructor has to be called manually.

extern "C"
{
    int sizeof_childA() { return sizeof(childA); }

    void childA_construct2(void* ptr_buffer) { new (ptr_buffer)childA(/*constructor params*/); }
    void childA_destruct2(void* ptr_buffer) { static_cast< childA* >(ptr_buffer)->~childA(); }
}

If you want to store and use function pointer for C, then to declare a function type:

extern "C" typedef unsigned MyFuncType(struct Msg*);

then the variable can stored as:

MyFuncType func;

Upvotes: 0

Matthieu Brucher
Matthieu Brucher

Reputation: 22023

Unobjectifying the code is quite simple to do:

#ifdef __cplusplus
extern "C"
{
#endif
    void* construct_me(/*arguments*/);
    void* get_object(void* obj);
    void delete_me(void* obj);
#ifdef __cplusplus
}
#endif

And then define them:

extern "C"
{
    void* construct_me(/*arguments*/)
    {
        return static_cast<void*>(new parentB(/*arguments*/));
    }
    void* get_object(void* obj)
    {
        return static_cast<void*>(&(static_cast<parentB*>(obj)->object));
    }
    void delete_me(void* obj)
    {
        delete static_cast<parentB*>(obj);
    }
}

If the type can be used in C, then you can just do:

Type get_object(void* obj)
{
    return static_cast<parentB*>(obj)->object;
}

instead of casting it to void*.

Inheritance doesn't change a thing. It's the same mechanism, except that if you have virtual functions, you should still wrap all of them for the inherited class (it's UB to transform a A* to void* to B* even if A inherits from B).

P.S.: I don't think this is any different than the answers in the link that was provided.

Upvotes: 2

Related Questions