2

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.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
emitrax
  • 394
  • 1
  • 4
  • 12

4 Answers4

5

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
4

Second case may be replaced by

std::conditional<(SOME_CONSTANT > SOME_VALUE), uint8_t, uint16_t>::type
alywasHereButDifferentSize;

First i think no.

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • I see that much of the attention went to the second case, which is fine, but I was actually more interested in the first case. Thanks anyway for your suggestion. – emitrax Jul 08 '12 at 17:35
1

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.

0

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.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • This is great, but it`s not solution, since HAS_MAYBE_HERE not defined - code will not compile. So, i think we can`t replace #ifdef at all. – ForEveR Jul 07 '12 at 16:30
  • 1
    @ForEveR: of course. One has to use some preprocessor at some point, like `#ifdef WHATEVER #define HAS_MAYBE_HERE 1 #else ...`, maybe even something like `#define HAS_MAYBE_HERE defined(WHATEVER)` would work. However, this is the standard way to add a member to a class metaprogrammatically. – Alexandre C. Jul 07 '12 at 16:33
  • Yeah, so only preprocessor magic can replace #ifdef in struct. So, i think it`s more overhead, than use one #ifdef. – ForEveR Jul 07 '12 at 16:38
  • @ForEveR: Or one could avoid `#ifdef` in favor of `#define` for this purpose. – Alexandre C. Jul 07 '12 at 17:24