1

I try to access in a C program data received from a C++ library which has been originally designed with structure inheritance:

example:

// C++ data structures
typedef struct _Base
{
public:
    int id;
    wchar_t* name;
} Base;


typedef struct _Struct1 : Base
{
public:
    int valueCount;
} Struct1;


typedef struct _Struct2 : Base
{
public:
    int parentID;
    int amount;
} Struct2;

I tried using the following data structures in C for mapping.

typedef struct _Base
{
    int id;
    wchar_t* name;
} Base;


typedef struct _Struct1
{
    // Base struct data
    int id;
    wchar_t* name;

    int valueCount;
} Struct1;


typedef struct _Struct2
{
    // Base struct data
    int id;
    wchar_t* name;

    int parentID;
    int amount;
} Struct2;

But printing the data, it looks like I get wrong values.

Am I missing something, any reference on how C++ represents inherited structure internally?

Thanks in advance!

Patrick Allaert
  • 1,751
  • 18
  • 44
  • 1
    Which values are wrong? Be more specific, please. – Matt May 31 '12 at 19:09
  • Are you using an integrated development environment such as visual studio? If you can debug the code, maybe your tools will allow you to view the raw bytes of the memory you're receiving and you can work out what doesn't match with what you're expecting. – Scott Langham May 31 '12 at 19:10
  • @Patrick: Are you able to modify the C++ code? – Scott Langham May 31 '12 at 19:15
  • @ScottLangham Nope I don't have it, only a header file as information, I only have it as a lib. – Patrick Allaert May 31 '12 at 19:20
  • @Matt Looks like printing the id is ok, but not the name. – Patrick Allaert May 31 '12 at 19:21
  • @Patrick: How does your C program link to the C++ library. Is the C++ library a dll? Or does your C program receive the data from a file or some type of communications link? Which platform/device are you using? – Scott Langham May 31 '12 at 19:42

4 Answers4

3

The C++11 rules on PODs (What are Aggregates and PODs and how/why are they special?) specify that don't allow mixing concrete base classes with data members, but in practice for most compilers having a single POD base class is equivalent to encapsulating that class as the first member.

Try specifying your C structs encapsulating the base struct:

typedef struct _Struct1
{
    Base base;
    int valueCount;
} Struct1;

Note that this won't work if the C++ classes are non-POD (e.g. have virtual methods).

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I thought about doing this as well but in terms of API, it doesn't really make sense to access some property via base, and valueCount directly. – Patrick Allaert May 31 '12 at 19:25
  • Does encapsulating the structure make a difference in terms of internal representation? – Patrick Allaert May 31 '12 at 19:25
  • It shouldn't really make a difference, but it's a place to start. Are you sure that pointers are the same size and wchar_t the same length between your platforms? – ecatmur May 31 '12 at 19:28
  • You pointed me into the right direction. It looks like a problem of wchar_t lenght in the end! The library is compiled for 64bit as well as my program. However, while debugging, it looks like my wchar_t string is composed of wide characters of 16bits as if it was compiled on 32bit system. I know have to figure how to print a string of 16bit wchar_t while in 64bits... – Patrick Allaert May 31 '12 at 20:32
  • 1
    No, C++11 doesn't specify that layout. From the link you give (and also from the standard), one of the conditions for a class to have standard layout is that it "either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members"; it has nothing to say about the relative layout of base-class members and derived-class members. – Mike Seymour Jun 01 '12 at 00:55
2

You might be able to trick your C compiler to produce a structure with the same memory layout of a C++ class, and in many cases that will work, but this is a fairly shaky foundation to build on.

The portable way would be to write accessor functions. Be sure to declare them as extern "C".

Illustrative example for a header file (not necessarily clean; typical caveats of writing code in a web form apply :-)):

#ifdef __cplusplus

// C++ declarations go here

class Foo
{
public:
   int bar;
};

// C calling conventions follow

extern "C" {

#else

// Make it so your C code can work with Foo* as an incomplete/not-dereferencable
// type.
typedef void Foo;

#endif

// Declare this in a C++ source file to return fooptr->bar
int foo_get_bar(Foo *fooptr);

#ifdef __cplusplus
} // extern "C"
#endif
asveikau
  • 39,039
  • 2
  • 53
  • 68
  • Can't change the C++ source code, only having a header file as reference. – Patrick Allaert May 31 '12 at 19:23
  • @PatrickAllaert - Please read more to get the meaning, not in "what can I copy and paste?" mode. I am not advocating changing C++ source code, only demonstrating a technique. If the class is in another header just replace "`class Foo { /* ... */ };`" with the right `#include`. The key point is you write your own `foo_get_bar()` which is a C++ function callable from C. – asveikau May 31 '12 at 19:25
1

One possibility to investigate is that the structs you've defined are correct, but that the aligmnents the compilers are using don't match. See:

http://en.wikipedia.org/wiki/Data_structure_alignment

Usually you can set the alignment using compiler switches or #pragma directives. You'd have to read your compiler documentation to find out about that. If you can't contact the authors of the C++ code, you may have to use a debugger to look at the raw memory you're receiving to work out what the alignment/padding is between the different values in the structures.

You can get this kind of problem if one program is compiled for a 64 bit machine and the other for a 32 bit machine. Or it could be down to the different compilers implementations.

Scott Langham
  • 58,735
  • 39
  • 131
  • 204
0

You can try using a wrapper layer over the C++ library you are using to output values coming from the C++ to to C rather than using directly what is coming from C++ side. That could work if the overhead from the extra layer would not be significant.

Casting a C++ class might not always work as it keeps member and virtual methods pointers inside the class structure.

rgngl
  • 5,353
  • 3
  • 30
  • 34