0

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;
Svalorzen
  • 5,353
  • 3
  • 30
  • 54
  • 1
    Why not `return union.theCorrectMember;`? (Assuming that you replace `union` with something that is actually a valid identifier) – Quentin Jan 11 '18 at 19:13
  • The union is nested inside a structure, and finding it requires the user to know internals which do not concern him/her. The point here is that I'm writing an API which I'd like to be consistent. If it's not possible, that's ok and I'll just require the user to know these particular internals, but the point is I'm trying to avoid that where possible (since they are generated and ugly). – Svalorzen Jan 11 '18 at 19:18
  • since you already know the member types, just provide getters for all possible union values – Ryan Jan 11 '18 at 19:26
  • Are you absolutely certain all your union members are at offset 0? Where are the tags actually defined? Inside the union member? I suspect the best you can hope for is a type check that the returned type matches the tag (type), and the returned member reference matches that type. – Gem Taylor Jan 11 '18 at 19:26
  • @RyanMcCullagh I don't. This is generated code. The wrapper must be good for code generated for any possible input. I know how the generated code is structured, not the specific names of the fields/types. – Svalorzen Jan 11 '18 at 19:29
  • @GemTaylor I am not, it's a normal union. I don't know what guarantees I can get from that, hence this question. I can attach a generated snipped to give you an idea of the code. The generation process creates a new type per each union containing a set of tags. A field of this new type is stored in a struct together with the union. I am providing an API to check the tag so the user can deal with that, I just wanted to also try to provide the value without the user knowing the internals of the C struct. – Svalorzen Jan 11 '18 at 19:32

1 Answers1

1

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;
  }
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524