简体   繁体   English

具有预处理器分支实现的结构是否存在ODR违规?

[英]Are structs with preprocessor branched implementation an ODR violation?

In a project that uses both C and C++, a .h file contains a definition of a type. 在同时使用C和C ++的项目中, .h文件包含类型的定义。 If that definition depends on whether the header is included by c or cpp files, am I violating the one definition rule? 如果该定义取决于标头是否包含在ccpp文件中,我是否违反了一个定义规则?

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

I know that ODR has to do with different translation units, but in "my case" won't different translation units end up having different implementations for a common struct? 我知道ODR与不同的翻译单元有关,但在“我的情况”中,不同的翻译单元最终会有不同的常见结构实现吗? I've seen this in production code and initially I was wondering what is the linker doing in this case. 我在生产代码中看到了这一点,最初我想知道链接器在这种情况下做了什么。

Any thoughts? 有什么想法吗?

As long as you use one compiler (C or C++), you won't have a problem. 只要您使用一个编译器(C或C ++),就不会有问题。 It doesn't matter what extension the header files have. 头文件的扩展名无关紧要。

But if you're linking together translation units from different languages, then yes you're violating the ODR. 但是,如果您将来自不同语言的翻译单元链接在一起,那么是的,您违反了ODR。

Overall this just seems really error prone. 总的来说,这似乎真的很容易出错。 I'd give the C++ type an entirely different name. 我给C ++类型一个完全不同的名字。 You can use your macro to switch between the two, perhaps using the preprocessor around a typedef ? 您可以使用宏在两者之间切换,也许使用typedef周围的预处理器?

There are two cases: 有两种情况:

  • All the translation units that include the header (for a given program) are compiled as the same language (C or C++): 包含标题(对于给定程序)的所有翻译单元都编译为相同的语言(C或C ++):

    ==> No problems. ==>没问题。

  • Some translation units that include the header are translated as C, some are translated as C++. 一些包含标题的翻译单元被翻译为C,一些翻译为C ++。

    ==> ODR violation. ==> ODR违规。

However, ODR violation is only "undefined behaviour", and actually, there isn't all that much which is defined in the standards about linking C and C++ together (except some vague suggestion "it ought to work"). 然而,ODR违反仅仅是“未定义的行为”,而实际上,没有所有有关链接C和C ++在一起(除了一些模糊的建议“它应该工作”)的标准定义的那么多。 In other words, if you are linking C and C++ together, you are probably depending on the details of your implementation anyway. 换句话说,如果要将C和C ++链接在一起,则可能依赖于实现的详细信息。

In general, if you are compiling 32-bit (so that std::size_t and unsigned are the same size), and provided that C++ does all the allocation, and provided that you never deal in arrays of these things in C, you will probably get away with it. 一般来说,如果你正在编译32位(这样std::size_tunsigned是相同的大小),并且假设C ++完成了所有的分配,并且假设你从不在C中处理这些东西的数组,你将会可能会侥幸逃脱。

Yes, I have a thought (hey, sorry, you asked): please don't write code that way. 是的,我有一个想法(嘿,对不起,你问过):请不要那样写代码。 Okay I'll reserve the correct way to do this till the end. 好的,我会保留正确的方法直到最后。 But as far as your question goes: yes, this will result in ODR violations if you use both a C and a C++ compiler as part of your build process. 但就你的问题而言:是的,如果在构建过程中同时使用C和C ++编译器,这将导致ODR违规。 The actual file extension may be irrelevant (it may change defaults for the compiler, but your build system may explicitly specify the language the compiler). 实际的文件扩展名可能无关紧要(它可能会更改编译器的默认值,但您的构建系统可能会明确指定编译器的语言)。 That said, this is a pretty bad idea and pretty unusual, because C is so close to being a proper subset of C++, that it would be much more common to simply write C code that can also build with a C++ compiler. 也就是说,这是一个非常糟糕的想法并且非常不寻常,因为C非常接近于成为C ++的正确子集,因此简单地编写也可以使用C ++编译器构建的C代码将更为常见。 And in the projects that have both C and C++ components, you would use a C++ compiler, and in projects that are pure C, you could still use that code. 在具有C和C ++组件的项目中,您将使用C ++编译器,而在纯C的项目中,您仍然可以使用该代码。 So, regardless of file extension, this is fine as long as a given project sticks to only 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 keeps as much code as possible in structs that are defined the same way and unconditionally regardless of macro options, and defers the macro related stuff to as late as possible. 这样就可以在结构中保留尽可能多的代码,这些结构以相同的方式无条件地定义,无论宏选项如何,并且尽可能延迟宏相关的东西。 This is also a big win in terms of tooling and testing, eg you can run unit tests for both versions of your struct without recompiling. 这在工具和测试方面也是一个巨大的胜利,例如,您可以在不重新编译的情况下对结构的两个版本运行单元测试。

It's not clear to me what violating the ODR means when you're linking multiple languages together; 当我将多种语言连接在一起时,我不清楚违反ODR的含义; the C++ standard has nothing to say about what struct s you can define in your C object files. C ++标准没有什么可说的,你可以在C对象文件中定义什么struct We are thus forced to answer in "assembly terms" (based on common implementations), in which case the answer (as has been given more eloquently already) is wat . 因此,我们被迫以“汇编术语”(基于通用实现)回答,在这种情况下,答案(已经更加雄辩地给出)是wat

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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