Svalorzen
Svalorzen

Reputation: 5608

Casting a union to one of its member types

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

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Related Questions