Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing struct to class (and other type changes) and ABI/code generation

Tags:

c++

abi

swig

It is well-established and a canonical reference question that in C++ structs and classes are pretty much interchangeable, when writing code by hand.

However, if I want to link to existing code, can I expect it to make any difference (i.e. break, nasal demons etc.) if I redeclare a struct as a class, or vice versa, in a header after the original code has been generated?

So the situation is the type was compiled as a struct (or a class), and I'm then changing the header file to the other declaration before including it in my project.

The real-world use case is that I'm auto-generating code with SWIG, which generates different output depending on whether it's given structs or classes; I need to change one to the other to get it to output the right interface.

The example is here (Irrlicht, SVertexManipulator.h) - given:

struct IVertexManipulator
{
};

I am redeclaring it mechanically as:

/*struct*/class IVertexManipulator
{public:
};

The original library compiles with the original headers, untouched. The wrapper code is generated using the modified forms, and compiled using them. The two are then linked into the same program to work together. Assume I'm using the exact same compiler for both libraries.

Is this sort of thing undefined? "Undefined", but expected to work on real-world compilers? Perfectly allowable?

Other similar changes I'm making include removing some default values from parameters (to prevent ambiguity), and removing field declarations from a couple of classes where the type is not visible to SWIG (which changes the structure of the class, but my reasoning is that the generated code should need that information, only to link to member functions). Again, how much havoc could this cause?

e.g. IGPUProgrammingServices.h:

s32 addHighLevelShaderMaterial(
    const c8* vertexShaderProgram,
    const c8* vertexShaderEntryPointName/*="main"*/,
    E_VERTEX_SHADER_TYPE vsCompileTarget/*=EVST_VS_1_1*/,
    const c8* pixelShaderProgram=0,
...

CIndexBuffer.h:

public:
    //IIndexList *Indices;

...and so on like that. Other changes include replacing some template parameter types with their typedefs and removing the packed attribute from some structs. Again, it seems like there should be no problem if the altered struct declarations are never actually used in machine code (just to generate names to link to accessor functions in the main library), but is this reliably the case? Ever the case?

like image 318
Leushenko Avatar asked May 29 '14 18:05

Leushenko


1 Answers

This is technically undefined behavior.

3.2/5:

There can be more than one definition of a class type, [... or other things that should be defined in header files ...] in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens; and

  • ...

... If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.

Essentially, you are changing the first token from struct to class, and inserting tokens public and : as appropriate. The Standard doesn't allow that.

But in all compilers I'm familiar with, this will be fine in practice.


Other similar changes I'm making include removing some default values from parameters (to prevent ambiguity)

This actually is formally allowed, if the declaration doesn't happen to be within a class definition. Different translation units and even different scopes within a TU can define different default function arguments. So you're probably fine there too.

Other changes include replacing some template parameter types with their typedefs

Also formally allowed outside of a class definition: two declarations of a function that use different ways of naming the same type refer to the same function.

... removing field declarations ... and removing the packed attribute from some structs

Now you're in severe danger territory, though. I'm not familiar with SWIG, but if you do this sort of thing, you'd better be darn sure the code using these "wrong" definitions never:

  • create or destroy an object of the class type

  • define a type that inherits or contains a member of the class type

  • use a non-static data member of the class

  • call an inline or template function that uses a non-static data member of the class

  • call a virtual member function of the class type or a derived type

  • try to find sizeof or alignof the class type

like image 69
aschepler Avatar answered Oct 18 '22 08:10

aschepler