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

时间:2017-05-05 09:20:01

标签: c++ c struct one-definition-rule

在同时使用C和C ++的项目中,package main import ( "encoding/json" "log" ) const ( inputJSON = `{ "id": 10, "result": [ { "bundled": true, "type": "RM-J1100" }, [ { "name": "PowerOff", "value": "AAAAAQAAAAEAAAAvAw==" }, { "name": "Input", "value": "AAAAAQAAAAEAAAAlAw==" } ] ] }` ) type Codes struct { Id int32 `json:"id"` Result [2]interface{} `json:"result"` } type Result struct { Info InfoMap Codes []Code } type InfoMap struct { Bundled bool `json:"bundled"` Type string `json:"type"` } type Code struct { Name string `json:"name"` Value string `json:"value"` } func main() { newCodes := &Codes{} err := json.Unmarshal([]byte(inputJSON), newCodes) if err != nil { log.Fatal(err) } // Prints the whole object log.Println(newCodes) // Prints the Result array (!) log.Println(newCodes.Result) if len(newCodes.Result) != 2 { log.Fatal("Invalid Result struct") } // Marshal and Unmarshal data to obtain the code list byteCodeList, _ := json.Marshal(newCodes.Result[1]) codeList := make([]Code, 0) err = json.Unmarshal(byteCodeList, &codeList) if err != nil { log.Fatal("Invalid Code list") } // Prints the codeList log.Println(codeList) } 文件包含类型的定义。如果该定义取决于标头是否包含在.hc文件中,我是否违反了一个定义规则?

cpp

我知道ODR与不同的翻译单元有关,但在“我的情况”中,不同的翻译单元最终会有不同的常见结构实现吗?我在生产代码中看到了这一点,最初我想知道链接器在这种情况下做了什么。

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

只要您使用一个编译器(C或C ++),就不会有问题。头文件的扩展名无关紧要。

但如果您将来自不同语言的翻译单元链接在一起,那么是的,您违反了ODR。

总的来说,这似乎真的很容易出错。我给C ++类型一个完全不同的名字。您可以使用宏在两者之间切换,也许使用围绕typedef

的预处理器

答案 1 :(得分:3)

有两种情况:

  • 包含标题(对于给定程序)的所有翻译单元都编译为相同的语言(C或C ++):

    ==>没问题。

  • 包含标题的一些翻译单元翻译为C,有些翻译为C ++。

    ==> ODR违规。

但是,ODR违规只是“未定义的行为”,实际上,标准中关于将C和C ++链接在一起的 并不是那么多(除了一些模糊的建议“它应该工作“)。换句话说,如果要将C和C ++链接在一起,则可能依赖于实现的详细信息。

通常,如果您正在编译32位(以便std::size_tunsigned具有相同的大小),并且假设C ++完成所有分配,并且假设您从未处理过数组在C语言中,你可能会侥幸逃脱。

答案 2 :(得分:2)

是的,我有一个想法(嘿,对不起,你问过):请不要那样写代码。好的,我会保留正确的方法直到最后。但就你的问题而言:是的,如果在构建过程中同时使用C和C ++编译器,这将导致ODR违规。实际的文件扩展名可能无关紧要(它可能会更改编译器的默认值,但您的构建系统可能会明确指定编译器的语言)。也就是说,这是一个非常糟糕的想法并且非常不寻常,因为C非常接近于成为C ++的正确子集,因此简单地编写也可以使用C ++编译器构建的C代码将更为常见。在具有C和C ++组件的项目中,您将使用C ++编译器,而在纯C的项目中,您仍然可以使用该代码。因此,无论文件扩展名如何,只要给定项目只支持一个编译器,这就没问题。

// 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>;

这样可以在结构中保留尽可能多的代码,这些结构以相同的方式无条件地定义,无论宏选项如何,并且尽可能延迟宏相关的内容。这在工具和测试方面也是一个巨大的胜利,例如您可以为结构的两个版本运行单元测试而无需重新编译。

答案 3 :(得分:1)

当我将多种语言连接在一起时,我不清楚违反ODR的含义是什么; C ++ 标准对于您可以在 C 目标文件中定义的struct无关。因此,我们被迫回答&#34;汇编条款&#34; (基于常见的实现),在这种情况下,答案(已经更加雄辩地给出)是 wat