Reputation: 5608
I'm stuck with some generated C code which is a bit messy. I'm currently wrapping it into a C++11 interface in order not to go insane.
The C code contains tagged unions. I'd love to be able to provide a simple method to read them, something like:
template <typename U, typename T>
T readTaggedUnion(const U & un) {
return static_cast<T>(un);
}
Is this legal?
I would also love to be able to do sanity checks on this, but it looks like type_traits
doesn't provide much in terms of unions. And all tags for the unions have their own generated types so there's no way to read those in a generic manner.
I have read c++ casting a union to one of its member types but it doesn't help me.
Edit: The code I have is generated based on some input files, which can change, and I have no control on those. I just know how the output will be structured, so I know which parts will always be the same and which depend on the input files.
An example of the generated code for a structure named TestChoice which can contain 4 different things inside:
/* Dependencies */
typedef enum TestChoice_PR {
TestChoice_PR_NOTHING, /* No components present */
TestChoice_PR_integer,
TestChoice_PR_optional,
TestChoice_PR_enm,
TestChoice_PR_setof
} TestChoice_PR;
/* TestChoice */
typedef struct TestChoice {
TestChoice_PR present;
union TestChoice_u {
INTEGER_t integer;
TestOptional_t optional;
TestEnumType_t enm;
TestSetOf_t setof;
} choice;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx; // This is not relevant
} TestChoice_t;
Upvotes: 0
Views: 1380
Reputation: 275405
I'd be tempted to return a boost::variant<Ts...>
(or if you can upgrade to C++17, std::variant
).
In any case
// quick test to ensure we aren't casting from a non-union:
template<class U>
void union_test( U const volatile& ) {
static_assert( std::is_union< U >::value, "only unions allowed as an argument" );
}
template<class T, class U>
T union_cast( U const& u ) {
union_test(u);
using pT = typename std::remove_reference<T>::type;
return std::forward<T>(*(pT const*)std::addressof(u));
}
template<class T, class U>
T union_cast( U const&& u ) {
union_test(u);
using pT = typename std::remove_reference<T>::type;
return std::forward<T>(*(pT const*)std::addressof(u));
}
template<class T, class U>
T union_cast( U&& u ) {
union_test(u);
using pT = typename std::remove_reference<T>::type;
return std::forward<T>(*(pT*)std::addressof(u));
}
use:
struct pt {
int x,y;
};
union x {
int a;
double d;
pt p;
};
void foo( int type, x in ) {
switch(type) {
case 0:
std::cout << union_cast<int>(in) << "\n";
return;
case 1:
std::cout << union_cast<double>(in) << "\n";
return;
case 2:
std::cout << union_cast<pt>(in).x << "," << union_cast<pt>(in).y << "\n";
return;
}
}
Upvotes: 1