7

For example, if I were to create a hierarchical static const struct like this in a header (.h) file:

static const struct {
    struct {
        char STATIC /* = 0 */;
        char DYNAMIC /* = 1 */;
    } ALLOCATION;
    struct {
        char TABLE /* = 0 */;
        char LIST /* = 1 */;
        char TREE /* = 2 */;
    } STRUCTURE;
} FOO_STRATEGY = { {0, 1}, {0, 1, 2} };

foo_t *foo_create(char allocation_strategy, char structure_type);

Which would then be used something like this:

foo_t *foo = foo_create(FOO_STRATEGY.ALLOCATION.STATIC, FOO_STRATEGY.STRUCTURE.TREE);

I guess I have a two-part question:

  1. Does this work as one would expect?
  2. Why don't other people do this?
Matt
  • 21,026
  • 18
  • 63
  • 115
  • 5
    How is this superior to using an `enum`? – 5gon12eder Jan 27 '15 at 22:49
  • @5gon12eder enums have global scope. – Matt Jan 27 '15 at 22:50
  • 1
    @Matt Well yes, but is it better to have `FOO_STRATEGY.ALLOCATION.STATIC` in your scope (due to including your header file) or `FOO_STRATEGY_ALLOCATION_STATIC` as a define or enum value? – Xaqq Jan 27 '15 at 22:53
  • @Xaqq I think `FOO_STRATEGY.ALLOCATION.STATIC` is better in terms of readability and usability. – Matt Jan 27 '15 at 22:54
  • 1
    Except that this will be a headscratcher for people having to use this header. Is it C ? C++ ? I believe using standard patterns is an acceptable tradeoff to polluting the global namespace. – SirDarius Jan 27 '15 at 22:56
  • 1
    @Matt This is up to you indeed. Being lazy I'd go the simpler way of `enum` or `define`. I think your solution is fine tho. – Xaqq Jan 27 '15 at 22:59
  • @SirDarius: I don't think people do this in C++ any more than they do in C. C++ has its own constructs, such as static members that make this unnecessary. So no one would confuse this with C++. – Matt Jan 27 '15 at 22:59
  • 1
    It would reduce the chance of a naming collision simply because nobody else would be crazy enough to use such a convoluted strategy :) – Rufflewind Jan 27 '15 at 23:19

3 Answers3

3

I see four possible problems. If you don't care about any of this, go for it.

  1. You lose the ability to do things like this example, where enum values are used to "name" indices into an array, and the last entry in the enum gives array size automatically.

    typedef enum {
       COLOR_RED,
       COLOR_GREEN,
       COLOR_BLUE,
       NUM_COLORS  
    } Color;
    
    
    int myPallete[NUM_COLORS];
    myPallete[COLOR_RED] = 0xf80000;
    

    There are likely other ways you might use a #define or enum as a constant in such a way that your approach doesn't work in C. This was just the first to come to mind.

  2. Declaring the struct in the header that way puts a copy of the struct in every object file generated. The wasted memory makes this unfriendly to deeply embedded systems where 5 bytes or RAM wasted per object file would suck. Putting your structs in ONE ".c" file and declaring it in the header with extern might help here.

  3. Nobody else does this, so you're creating code that will trip up anybody else looking at your code for the first time, though I imagine it would not be hard to adjust. I don't care what your opinion is here. If multiple commentators said this would be confusing, you've already confused those people.

  4. This does little (maybe nothing?) to prevent name collisions. It makes no difference whether a header instantiates this struct or does #define FOO_STRATEGY. Anything that includes that header has equal possibility of name collisions if you use good prefixes on enum/#define values.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Brian McFarland
  • 9,052
  • 6
  • 38
  • 56
  • 1: Thanks for your input. 2: Variables declared as `static const` would be optimized out by any sane compiler. 3: I never said that it won't confuse people, only that I don't think people would think it's not C. 4: Thanks for your input. – Matt Jan 27 '15 at 23:16
  • 3
    @Matt, RE #2, you're probably right in general. But unfortunately, many compilers in the deeply embedded world (think $0.10 8-bit MCUs with 2kB of RAM) are far from sane. I'm curious what microchip's PIC compilers would do for example. – Brian McFarland Jan 27 '15 at 23:22
2

People don't do this because that's what enum is for.

Doug Currie
  • 40,708
  • 1
  • 95
  • 119
  • But enums have global scope. – Matt Jan 27 '15 at 22:50
  • 2
    But the difference between `FOO_STRATEGY.ALLOCATION.STATIC` and `FOO_STRATEGY_ALLOCATION_STATIC` is so tiny, it's really nonexistant in the real world. If it really bothers you this much, use C++'s namespaces. – Daniel Kamil Kozar Jan 27 '15 at 22:55
  • @DanielKamilKozar: C++ namespaces are not available in C, and they still don't solve this problem as they cannot be nested. – Matt Jan 27 '15 at 22:56
  • 1
    @Matt Actually with `enum class` you can do nice thing like `ns1::ns2::enum_type::value`. – Xaqq Jan 27 '15 at 23:00
  • @Xaqq: C++ namespaces can't be nested, but classes with static members can be used instead. – Matt Jan 27 '15 at 23:02
  • 2
    What do you mean "can't be nested"? `namespace foo { namespace strategy { namespace allocation { static const int STATIC = 1; static const int DYNAMIC = 2; } } }` works just fine, unless I understood you incorrectly. Of course, that solution won't work for you because of the language choice, but it's nevertheless valid. :) – Daniel Kamil Kozar Jan 27 '15 at 23:05
  • @DanielKamilKozar: For some reason I had thought they couldn't be, but a quick Google search proves me wrong. I don't know why I thought that. – Matt Jan 27 '15 at 23:06
  • 1
    @Matt *But enums have global scope* enumeration constants are identifiers and they have different lexical scopes depending on where they are declared. – ouah Jan 27 '15 at 23:19
2

I suppose this would work, yes. However, it would increase the binary size (unless optimized away by our friend the compiler).

I guess people do not use it, because using properly formated define is easier.

#define FOO_STRATEGY_ALLOCATION_STATIC  1
#define FOO_STRATEGY_ALLOCATION_DYNAMIC 2

You could also use an enum.

enum foo_strategy_allocation
{
 FOO_STRATEGY_ALLOCATION_STATIC = 1,
 FOO_STRATEGY_ALLOCATION_DYNAMIC = 2
};
Xaqq
  • 4,308
  • 2
  • 25
  • 38