handmade.network » Forums » Alternative C/C++ type definitions
Ciryus33
Jason Smith
8 posts
#16383 Alternative C/C++ type definitions
2 months, 2 weeks ago Edited by Jason Smith on Sept. 24, 2018, 2:20 a.m. Reason: Initial post

I was messing around and expecting the following to blow up when I tried to compile it but to my shock it totally works. Can someone please explain to me how/why the following works.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// foo.h
#ifndef FOO_H
#define FOO_H

#ifdef __cplusplus
#include <iostream>
extern "C" {
#endif

typedef struct foo {
    float x;
    float y;
    float z;

#ifdef __cplusplus
    float w;

    inline foo operator+(const foo& addend) {
        foo result = {
            x + addend.x,
            y + addend.y,
            z + addend.z,
            w + addend.w
        };
        return result;
    }
#endif

}foo;

#ifdef __cplusplus

} // End C linkage

inline std::ostream& operator<<(std::ostream &os, const foo &inst) {
    os << inst.x << ' ' << inst.y << ' ' << inst.z << ' ' << inst.w << '\n';
    return os;
}

#endif

#endif // FOO_H

//cfoo.c
#include "foo.h"
extern foo c_foo;
foo c_foo = { 3.0f, 3.0f, 3.0f };

// main.cpp
#include "foo.h"
#include <iostream>

extern "C" foo c_foo;
static foo cpp_foo = { 1.0f, 2.0f, 3.0f, 4.0f };
int main(){
    foo a = cpp_foo + c_foo;
    std::cout << c_foo;
    std::cout << cpp_foo;
    std::cout << a;

    float w = a.w;
    std::cout << w << std::endl;

    std::getchar();
    return 0;
}


This certainly seems like a disaster waiting to happen, especially since sizeof() gives a different value depending on if it is used in a .c or .cpp file. I was expecting a multiple definition error but MSVC, Clang, and G++ all compiled and ran the program just fine. I would like to understand why.
mmozeiko
Mārtiņš Možeiko
1831 posts / 1 project
#16384 Alternative C/C++ type definitions
2 months, 2 weeks ago Edited by Mārtiņš Možeiko on Sept. 24, 2018, 2:36 a.m.

ODR rule. If you violate it, you get undefined behavior.

From C++ standard (3.2 One Definition Rule):

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template.

[...]

There can be more than one definition of a class type [...] 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.


Your definition of foo struct has different sequence of tokens in different translation units. Thus undefined behavior.
Ciryus33
Jason Smith
8 posts
#16385 Alternative C/C++ type definitions
2 months, 2 weeks ago

I also noticed the following on cppreference:

the language linkage is the same (e.g. the include file isn't inside an extern "C" block)


I'm thinking that this also contributes to this being undefined?