I have to generate a data structure that contains certain fields only under certain condition. This typically always translates to something like the following
struct MyStruct {
int alwaysHere;
#ifdef WHATEVER
bool mightBeHere;
#endif
char somethingElse;
#if SOME_CONSTANT > SOME_VALUE
uint8_t alywasHereButDifferentSize;
#else
uint16_t alywasHereButDifferentSize;
#endif
...
};
From my point of view this gets easily ugly to look at, and unreadable.
Without even talking about the code that handle those fields, usually under
ifdefs too.
I'm looking for an elegant way to achieve the same result without adding any overhead whatsoever, but with a code much
more readable. Template specialization seems a bit excessive, but it seems to me
to be the only alternative.
Is C++11 adding anything at all to deal with this situation?
Any suggestion would be appreciated.
For the second case, I'd usually prefer a typedef that restricts the hackery to one place:
#if SOME_CONSTANT > SOME_VALUE
typedef uint8_t always_type;
#else
typedef uint16_t always_type;
#endif
Then the rest of your code will just use always_type
throughout:
struct MyStruct {
// ...
always_type always_here_but_different_size;
// ...
};
If you want to use:
typedef std::conditional<(SOME_CONSTANT > VALUE), uint8_t, uint16_t>::type always_type;
That's fine too -- the point here isn't about the syntax you use to get the type you want, but the fact that you generally want to create a name for that type so you can use it where needed.
As for the situation of something being present or not, it's a little hard to say. Typically, such a thing will relate to enabling/disabling certain features at build time. If so, it appears that the class has responsibilities related both to the feature(s) that can be enabled/disabled, and to something else as well. That sounds like it's probably violating the single responsibility principle, and may not be very cohesive. If that's the case, it may indicate a problem that's better addressed at the level of the overall design, than simply the syntax you use.
Caveat: I'm probably extrapolating quite a bit from admittedly minimal evidence -- possibly more than the evidence really supports.
Second case may be replaced by
std::conditional<(SOME_CONSTANT > SOME_VALUE), uint8_t, uint16_t>::type
alywasHereButDifferentSize;
First i think no.
I've seen this done with a union (http://www.cplusplus.com/doc/tutorial/other_data_types/), but I'm unsure about the specifics as I've never implemented it myself. Instead of the second piece of macrohackery, you would have:
union {
uint8_t alywasHereButDifferentSize;
uint16_t alywasHereButDifferentSize;
}
and when referencing, you would have an if that referred to one or the other variable.
Alternatively, you could make a void * pointer that you initialise during runtime to a variable of either type.
For the first:
template <bool>
struct implement_maybe_here
{};
template <>
struct implement_maybe_here<true>
{
int maybe_here;
};
struct my_struct : implement_maybe_here<HAS_MAYBE_HERE>
{
double always_here;
};
I don't recommend this in any way, since it is difficult to maintain and the maybe_here
variable is difficult to initialize (but with any approach, this will be a mess anyway).
Note that you are not guaranteed that implement_maybe_here<false>
will take zero memory, however many compilers implement this "empty base optimization".
Use std::conditional
for the second one, as @ForEveR suggests.