Skip to content

Commit a31e870

Browse files
committed
Add support for tagged enums that turn into unions.
1 parent 5870924 commit a31e870

File tree

9 files changed

+304
-17
lines changed

9 files changed

+304
-17
lines changed

src/bindgen/ir/enumeration.rs

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -719,10 +719,13 @@ impl Source for Enum {
719719
}
720720
// Done emitting the enum
721721

722-
// Emit an ostream function if required, but for now we only support
723-
// untagged enums because tagged ones get a little tricky...
722+
// Emit an ostream function if required.
724723
let derive_ostream = config.enumeration.derive_ostream(&self.annotations);
725-
if !is_tagged && config.language == Language::Cxx && derive_ostream {
724+
if config.language == Language::Cxx && derive_ostream {
725+
// For untagged enums, this emits the serializer function for the
726+
// enum. For tagged enums, this emits the serializer function for
727+
// the tag. In the latter case we need a couple of minor changes
728+
// due to the function living inside the top-level struct or enum.
726729
let stream = config
727730
.function
728731
.rename_args
@@ -734,14 +737,18 @@ impl Source for Enum {
734737

735738
out.new_line();
736739
out.new_line();
737-
// We mark the function inline because the header might get included
738-
// into multiple compilation units that get linked together, and not
739-
// marking it inline would result in multiply-defined symbol errors.
740+
// For untagged enums, we mark the function inline because the
741+
// header might get included into multiple compilation units that
742+
// get linked together, and not marking it inline would result in
743+
// multiply-defined symbol errors. For tagged enums we don't have
744+
// the same problem, but mark it as a friend function of the
745+
// containing union/struct.
740746
write!(
741747
out,
742-
"inline std::ostream& operator<<(std::ostream& {}, const {}& {})",
748+
"{} std::ostream& operator<<(std::ostream& {}, const {}& {})",
749+
if is_tagged { "friend" } else { "inline" },
743750
stream,
744-
self.export_name(),
751+
enum_name,
745752
instance,
746753
);
747754

@@ -753,8 +760,13 @@ impl Source for Enum {
753760
.iter()
754761
.map(|x| {
755762
format!(
756-
"case {}::{}: {} << \"{}\"; break;",
757-
enum_name, x.export_name, stream, x.export_name
763+
"case {}{}{}::{}: {} << \"{}\"; break;",
764+
if is_tagged { self.export_name() } else { "" },
765+
if is_tagged { "::" } else { "" },
766+
enum_name,
767+
x.export_name,
768+
stream,
769+
x.export_name
758770
)
759771
})
760772
.collect();
@@ -764,6 +776,56 @@ impl Source for Enum {
764776

765777
write!(out, "return {};", stream);
766778
out.close_brace(false);
779+
780+
if is_tagged {
781+
// For tagged enums, this emits the serializer function for
782+
// the top-level enum or struct.
783+
out.new_line();
784+
out.new_line();
785+
write!(
786+
out,
787+
"friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
788+
stream,
789+
self.export_name(),
790+
instance,
791+
);
792+
793+
out.open_brace();
794+
write!(out, "switch ({}.tag)", instance);
795+
out.open_brace();
796+
let vec: Vec<_> = self
797+
.variants
798+
.iter()
799+
.map(|x| {
800+
if let VariantBody::Body { ref name, .. } = x.body {
801+
format!(
802+
"case {}::{}::{}: {} << {}.{}; break;",
803+
self.export_name(),
804+
enum_name,
805+
x.export_name,
806+
stream,
807+
instance,
808+
name,
809+
)
810+
} else {
811+
format!(
812+
"case {}::{}::{}: {} << \"{}\"; break;",
813+
self.export_name(),
814+
enum_name,
815+
x.export_name,
816+
stream,
817+
x.export_name,
818+
)
819+
}
820+
})
821+
.collect();
822+
out.write_vertical_source_list(&vec[..], ListType::Join(""));
823+
out.close_brace(false);
824+
out.new_line();
825+
826+
write!(out, "return {};", stream);
827+
out.close_brace(false);
828+
}
767829
}
768830

769831
// If tagged, we need to emit structs for the cases and union them together

tests/expectations/both/derive_ostream.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,28 @@ typedef struct D {
2424
B Things;
2525
} D;
2626

27-
void root(A a, B b, C c, D d);
27+
enum F_Tag {
28+
Foo,
29+
Bar,
30+
Baz,
31+
};
32+
typedef uint8_t F_Tag;
33+
34+
typedef struct Foo_Body {
35+
F_Tag tag;
36+
int16_t _0;
37+
} Foo_Body;
38+
39+
typedef struct Bar_Body {
40+
F_Tag tag;
41+
uint8_t x;
42+
int16_t y;
43+
} Bar_Body;
44+
45+
typedef union F {
46+
F_Tag tag;
47+
Foo_Body foo;
48+
Bar_Body bar;
49+
} F;
50+
51+
void root(A a, B b, C c, D d, F f);

tests/expectations/both/derive_ostream.compat.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,41 @@ typedef struct D {
3030
B Things;
3131
} D;
3232

33+
enum F_Tag
34+
#ifdef __cplusplus
35+
: uint8_t
36+
#endif // __cplusplus
37+
{
38+
Foo,
39+
Bar,
40+
Baz,
41+
};
42+
#ifndef __cplusplus
43+
typedef uint8_t F_Tag;
44+
#endif // __cplusplus
45+
46+
typedef struct Foo_Body {
47+
F_Tag tag;
48+
int16_t _0;
49+
} Foo_Body;
50+
51+
typedef struct Bar_Body {
52+
F_Tag tag;
53+
uint8_t x;
54+
int16_t y;
55+
} Bar_Body;
56+
57+
typedef union F {
58+
F_Tag tag;
59+
Foo_Body foo;
60+
Bar_Body bar;
61+
} F;
62+
3363
#ifdef __cplusplus
3464
extern "C" {
3565
#endif // __cplusplus
3666

37-
void root(A a, B b, C c, D d);
67+
void root(A a, B b, C c, D d, F f);
3868

3969
#ifdef __cplusplus
4070
} // extern "C"

tests/expectations/derive_ostream.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,28 @@ typedef struct {
2424
B Things;
2525
} D;
2626

27-
void root(A a, B b, C c, D d);
27+
enum F_Tag {
28+
Foo,
29+
Bar,
30+
Baz,
31+
};
32+
typedef uint8_t F_Tag;
33+
34+
typedef struct {
35+
F_Tag tag;
36+
int16_t _0;
37+
} Foo_Body;
38+
39+
typedef struct {
40+
F_Tag tag;
41+
uint8_t x;
42+
int16_t y;
43+
} Bar_Body;
44+
45+
typedef union {
46+
F_Tag tag;
47+
Foo_Body foo;
48+
Bar_Body bar;
49+
} F;
50+
51+
void root(A a, B b, C c, D d, F f);

tests/expectations/derive_ostream.compat.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,41 @@ typedef struct {
3030
B Things;
3131
} D;
3232

33+
enum F_Tag
34+
#ifdef __cplusplus
35+
: uint8_t
36+
#endif // __cplusplus
37+
{
38+
Foo,
39+
Bar,
40+
Baz,
41+
};
42+
#ifndef __cplusplus
43+
typedef uint8_t F_Tag;
44+
#endif // __cplusplus
45+
46+
typedef struct {
47+
F_Tag tag;
48+
int16_t _0;
49+
} Foo_Body;
50+
51+
typedef struct {
52+
F_Tag tag;
53+
uint8_t x;
54+
int16_t y;
55+
} Bar_Body;
56+
57+
typedef union {
58+
F_Tag tag;
59+
Foo_Body foo;
60+
Bar_Body bar;
61+
} F;
62+
3363
#ifdef __cplusplus
3464
extern "C" {
3565
#endif // __cplusplus
3666

37-
void root(A a, B b, C c, D d);
67+
void root(A a, B b, C c, D d, F f);
3868

3969
#ifdef __cplusplus
4070
} // extern "C"

tests/expectations/derive_ostream.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,62 @@ struct D {
4747
}
4848
};
4949

50+
union F {
51+
enum class Tag : uint8_t {
52+
Foo,
53+
Bar,
54+
Baz,
55+
};
56+
57+
friend std::ostream& operator<<(std::ostream& stream, const Tag& instance) {
58+
switch (instance) {
59+
case F::Tag::Foo: stream << "Foo"; break;
60+
case F::Tag::Bar: stream << "Bar"; break;
61+
case F::Tag::Baz: stream << "Baz"; break;
62+
}
63+
return stream;
64+
}
65+
66+
friend std::ostream& operator<<(std::ostream& stream, const F& instance) {
67+
switch (instance.tag) {
68+
case F::Tag::Foo: stream << instance.foo; break;
69+
case F::Tag::Bar: stream << instance.bar; break;
70+
case F::Tag::Baz: stream << "Baz"; break;
71+
}
72+
return stream;
73+
}
74+
75+
struct Foo_Body {
76+
Tag tag;
77+
int16_t _0;
78+
79+
friend std::ostream& operator<<(std::ostream& stream, const Foo_Body& instance) {
80+
return stream << "{ " << "tag=" << instance.tag << ", "
81+
<< "_0=" << instance._0 << " }";
82+
}
83+
};
84+
85+
struct Bar_Body {
86+
Tag tag;
87+
uint8_t x;
88+
int16_t y;
89+
90+
friend std::ostream& operator<<(std::ostream& stream, const Bar_Body& instance) {
91+
return stream << "{ " << "tag=" << instance.tag << ", "
92+
<< "x=" << instance.x << ", "
93+
<< "y=" << instance.y << " }";
94+
}
95+
};
96+
97+
struct {
98+
Tag tag;
99+
};
100+
Foo_Body foo;
101+
Bar_Body bar;
102+
};
103+
50104
extern "C" {
51105

52-
void root(A a, B b, C c, D d);
106+
void root(A a, B b, C c, D d, F f);
53107

54108
} // extern "C"

tests/expectations/tag/derive_ostream.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,28 @@ struct D {
2424
struct B Things;
2525
};
2626

27-
void root(struct A a, struct B b, C c, struct D d);
27+
enum F_Tag {
28+
Foo,
29+
Bar,
30+
Baz,
31+
};
32+
typedef uint8_t F_Tag;
33+
34+
struct Foo_Body {
35+
F_Tag tag;
36+
int16_t _0;
37+
};
38+
39+
struct Bar_Body {
40+
F_Tag tag;
41+
uint8_t x;
42+
int16_t y;
43+
};
44+
45+
union F {
46+
F_Tag tag;
47+
struct Foo_Body foo;
48+
struct Bar_Body bar;
49+
};
50+
51+
void root(struct A a, struct B b, C c, struct D d, union F f);

tests/expectations/tag/derive_ostream.compat.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,41 @@ struct D {
3030
struct B Things;
3131
};
3232

33+
enum F_Tag
34+
#ifdef __cplusplus
35+
: uint8_t
36+
#endif // __cplusplus
37+
{
38+
Foo,
39+
Bar,
40+
Baz,
41+
};
42+
#ifndef __cplusplus
43+
typedef uint8_t F_Tag;
44+
#endif // __cplusplus
45+
46+
struct Foo_Body {
47+
F_Tag tag;
48+
int16_t _0;
49+
};
50+
51+
struct Bar_Body {
52+
F_Tag tag;
53+
uint8_t x;
54+
int16_t y;
55+
};
56+
57+
union F {
58+
F_Tag tag;
59+
struct Foo_Body foo;
60+
struct Bar_Body bar;
61+
};
62+
3363
#ifdef __cplusplus
3464
extern "C" {
3565
#endif // __cplusplus
3666

37-
void root(struct A a, struct B b, C c, struct D d);
67+
void root(struct A a, struct B b, C c, struct D d, union F f);
3868

3969
#ifdef __cplusplus
4070
} // extern "C"

0 commit comments

Comments
 (0)