Are preprocessor forked structs an ODR violation?

In a project that uses both C and C ++, the file .h

contains a type definition. If this definition depends on whether the header is included in the files c

or cpp

am I breaking one definition rule?

// my_header.h
struct MyStruct
{
#ifdef __cplusplus
    std::size_t member; 
    int surprise; 
#else
    unsigned member; 
#endif
};

      

I know the ODR has to do with different translation units, but in "my case" won't there be different translation units that have different implementations for the overall structure? I saw this in production code and was initially wondering what the linker does in this case.

Any thoughts?

+3


source to share


4 answers


As long as you use one compiler (C or C ++) you won't have a problem. It doesn't matter what extension the header files have.

But if you link translation units from different languages, then yes, you are breaking the ODR.



All in all it just seems prone to errors. I would call a C ++ type by a completely different name. Can you use your macro to switch between them, perhaps using the preprocessor around typedef

?

+4


source


There are two cases:

  • All translation units containing the title (for a given program) are compiled as the same language (C or C ++):

    ==> No problem.

  • Some translation units that include a title are translated as C, some of them are translated as C ++.

    ==> ODR violation.



However, the ODR violation is only "undefined behavior" and there really isn't much that is defined in the standards about combining C and C ++ together (except for some undefined "it should work" clause). In other words, if you are linking C and C ++ together, you are probably depending on the details of your implementation.

In general, if you compile 32-bit (so std::size_t

both unsigned

are the same size) and assuming C ++ does all the allocation and assuming you never deal with arrays of these things in C, you will probably go with him.

+3


source


Yes, I have a thought (hey, sorry you asked): please don't write your code this way. Ok, I'll reserve the correct way to do this until the end. But as for your question: yes, it will lead to ODR violations if you use both C and the C ++ compiler as part of the build process. The actual file extension may not be up to date (it may change the compiler defaults, but your build system may explicitly specify the compiler language). However, this is a rather bad idea and rather unusual because C is so close to being a proper subset of C ++ that it would be much easier to write C code that can also be built with a C ++ compiler. And in projects that have both C and C ++ components, you must use the C ++ compiler, and in projects that are pure C,you can still use this code. Thus, regardless of the file extension, it is fine if a given project only refers to one compiler.

// my_header.h
#ifdef __cplusplus
    constexpr bool is_cpp = true;
#else
    constexpr bool is_cpp = false;
#endif

struct cpp {
  std::size_t member;
  int surprise;
};

struct cc {
  unsigned member;
};

template <bool CPP>
struct MyStructImpl : private std::conditional_t<cpp, cc, CPP>
{
};

using MyStruct = MyStructImpl<is_cpp>;

      

This saves as much code as possible in structures that are defined the same and unconditionally regardless of macro parameters, and defer macro-related stuff as late as possible. It's also a big win in terms of tools and testing, for example. you can run unit tests for both versions of your framework without recompiling.

+2


source


It is not clear to me what the ODR is breaking when you link multiple languages ​​together; the C ++ standard has nothing to say about what struct

can be defined in your C object files, so we are forced to respond in "build conditions" (based on common implementations), in which case the answer (as said more eloquently) is wat ...

+1


source







All Articles