简体   繁体   中英

struct as a base class - C/C++ interoperability

I recall I saw somewhere some code which used to have a struct as a base class, and a C++ class as a derived class

struct Base_Struct
{

}

class Derived : Base_Struct
{
  ...
}

And the point is that a pointer to Base_Struct* was passed from the C++ files to some C files which then managed to use some function pointers in Base_Struct.

My question is: if I pass Base_Struct* to a C file, will the C code be able to use the Base_Struct completely? What about the derived class?

If I pass Base_Struct* to a C file, will the C code be able to use the Base_Struct completely?

If it's a standard-layout class, and the C compiler uses the same ABI for such classes as the C++ compiler, then it can access all the data members. Obviously, it couldn't access any member functions or static members, since such things don't exist in C and would have to be left out of any C-compatible definition of the structure.

What about the derived class?

You couldn't define that class in a C program, so it couldn't do anything interesting with the pointer.

Maybe it can work for the base class. But that is on very special case:

  • POD struct
  • same packing
  • same runtime if you allocate/deallocate in C and C++
  • pure C syntax
  • etc...

But it will never directly for the derived class.

But that create so many restriction that I would provide a C level API to allocate an manipulate those pointers defined as alias on "void*" (I'm not even sure you can do such aliases this in pure C).

Something like:

 typedef void* BaseStructPtr;

 BaseStructPtr AllocateBase(/* constructor params */);
 BaseStructPtr AllocateDerived(/* constructor params */);

 TypeOfField1 GetField1(BaseStructPtr ptr);
 TypeOfField2 GetField2(BaseStructPtr ptr);

 /* etc... */

I do not see any problem to derive from a structure declared in C:

C/C++

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    int a;
    int b;
} X;

void print(struct StructX* x) {
    printf("%d, %d\n", x->a, x->b);
}

#ifdef __cplusplus
} // extern "C"
#endif

C++

#include <iostream>

struct Y : StructX {
    int c;

    Y()
    : c(3)
    {
        a = 1;
        b = 2;
    }

    void print() {
        ::print(this);
        std::cout << c << std::endl;
    }
};


int main() {
    Y y;
    y.print();
}

Having the structure declared in C++ will complicate things. Now the struct might (or will) have information C is not aware of and(lets say) a memcpy could introduce undefined behavior. To avoid this you would have to wrap the functionality of the C++ structure in C:

C/C++

#ifdef __cplusplus
extern "C" {
#endif

typedef struct Z; // An opaque type (for X) to ensure minimum type safety.

extern int a(Z*);
extern int b(Z*);

#ifdef __cplusplus
} // extern "C"
#endif

C++

extern "C" {
    int a(Z* z) {
        return ((X*)z)->a;
    }
    int b(Z* z) {
        return ((X*)z)->b;
    }
} // extern "C"

In your sample yes a C compiler will be able to use the Base_Struct* pointer and access members. You can use the same struct definition in C and CPP files without problems. As long as it's the Cpp who cast the struct for the C file the compiler will do the job.

void Give_Typed_Pointer_to_C_Function(Base_Struct* pStruct) {...}
void Give_Pointer_to_C_Function(void* pStruct) {...}
Base_Struct A;
Derived B;
Give_Typed_Pointer_to_C_Function(&A);         // OK
Give_Typed_Pointer_to_C_Function(&B);         // OK 
Give_Pointer_to_C_Function(&A);               // OK    
Give_Pointer_to_C_Function(&B);               // wrong
Give_Pointer_to_C_Function((Base_Struct*)&B); // OK 

For this to work you have to use the same alignment and packing of struct but as you pass pointer I suppose you are same project so it should be already be the case.

You can even do more, if you keep struct in same order

struct S1 { int x; 
            int y;
          };
class C1 {  int x;
            int y;
            int add(int,int); 
          };
void Give_Typed_Pointer_to_C_Function(S1 *pStruct);
C1 object;
Give_Typed_Pointer_to_C_Function((S1*)&object);  // OK

// an safer alternative as you can't have to be sure that S1 and C1 match
struct C1 { int x;
            int y;
#ifdef __cplusplus
            int add(int,int); 
#endif
          };

If you put Cpp keyword like class/public/private and methods in struct with defines. And if you don't use inheritance it will work as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM