Enumerated type and RNG

Discussion in 'C++' started by mike3, Jan 1, 2010.

  1. mike3

    mike3 Guest

    Hi.

    How could I resolve this C++ programming dilemma?

    ---
    enum smth { ZERO, ONE, TWO, MAX }; // not really labeled this way

    void f(smth parameter)
    {
    ( ... )
    }

    ( ... )
    f(static_cast<smth>(RNG(MAX))); // RNG generates number from 0 to
    MAX-1
    ( ... )
    f(TWO); // f is called with fixed constant
    ( ... )
    f(666); // won't work -- good
    ( ... )
    ---

    Note the marked bit. f() takes the enumerated type smth, so you can't
    just stick "666" in there and cause something bad that way, yet I
    also need to pass a randomly obtained value from that list, which
    requires a cast. Is it considered good practice to use a cast in this
    situation? Would it be better to have f() take an "int" or something
    like
    that and then include an explicit check in there to see if the value
    is
    outside 0...MAX-1?

    Finally, I suppose one could also do this:
    int rand(RNG(MAX));
    if(rand == ZERO) f(ZERO);
    if(rand == ONE) f(ONE);
    if(rand == TWO) f(TWO);

    This avoids the cast, but seems to introduce a mantainability defect
    and
    making the code more susceptible to bugs: if one goes and adds more
    values to the enum, then must be added here, too (not just in f()),
    and
    if this is forgotten about... bug!

    What should be done here?
     
    mike3, Jan 1, 2010
    #1
    1. Advertisements

  2. mike3

    James Kanze Guest

    No, however...
    Formally, I guess you probably should use some sort of mapping
    array (indexed by the return value of rand), but in practice,
    I'd just cast. What I would do, however, is isolate it in a
    function, e.g.:

    smth
    rand( smth max )
    {
    return static_cast< smth >( rand( +max ); }
    // Note the unary +, to force max to an integral
    // type and avoid infinite recursion.
    }

    This way, you don't have casts all over the place.
     
    James Kanze, Jan 2, 2010
    #2
    1. Advertisements

  3. mike3

    mike3 Guest

    So does this mean that for every enum one wants to use the RNG
    to generate a value for, one would need tiny "rand" functions
    (inline, presumably)? Where would one put all that stuff?
     
    mike3, Jan 2, 2010
    #3
  4. mike3

    mike3 Guest

    Could one use a template? Like this?:

    ---
    template<class E>
    inline E rand(E max)
    {
    return(static_cast<E>(rand(+max)));
    }
     
    mike3, Jan 3, 2010
    #4
  5. mike3

    James Kanze Guest

    And how many is that? Formally, enum types don't support random
    generation in C++, so it's up to you to implement it if you need
    it. Just as you do for most other stuff concerning enums
    (iterators, | and & operators, etc.).
    Where ever you put the implementation of any other functions
    associated with the enum.

    Enums, in C++, are a bit funny. They're designed to fulfill at
    least two needs, and so do neither completely, since the
    operations required to fulfil one of the needs don't make sense
    when the enum is used for the other need. (Increment doesn't
    make sense when the enum is used to define bit masks, and & and
    | don't make sense when the enum is used to define an
    enumeration.)
     
    James Kanze, Jan 3, 2010
    #5
  6. mike3

    James Kanze Guest

    [...]
    Probably, but I'd worry about ambiguities when calling rand,
    potentially resulting in infinite recursion. Maybe if you put
    it in a special namespace, however.

    What we'd really like, of course, is some means of automatically
    getting the maximum value, so that you could write:
    SomeEnum e = rand< SomeEnum >();
    (This can be done if you explicitely instantiate
    std::numeric_limits for each enum type. Very elegant solution,
    but probably a case of the cure being worse than the disease;
    it's a lot of code to write.)
    Again, how often does the case occur?
     
    James Kanze, Jan 3, 2010
    #6
  7. mike3

    mike3 Guest

    Couldn't one also just call the RNG function something else,
    and then go through this to access it for different types?
    I suppose. It's easier to just have a "max" at the end of the enums.
    Not sure what the final number will be, but it'd be more than just
    one or two.
     
    mike3, Jan 4, 2010
    #7
  8. mike3

    James Kanze Guest

    Certainly. One could also define a template class, instantiated
    on the enum type, and call a member function of it. There are
    any number of solutions. (I would, in fact, use the template
    class solution. But mainly because my random number generator
    is already a class, designed for derivation to support such
    things.)
    Yes, but the problem is accessing it from a template. Enums
    don't (at present) introduce a new scope, so two templates in
    the same scope can't both define an element max. And if the
    name is different (to reflect the enum type), then you're
    screwed with regards to templates.
     
    James Kanze, Jan 4, 2010
    #8
  9. mike3

    mike3 Guest

    You mean like make the RNG bit a class itself, that you then declare
    variables as a type of? But then you still have the problem of
    getting the right max.
    How does the using different names "screw it" with regards to
    templates? You mean because one has to sort of "specify the type
    twice", once in the template, the next in the max? How can one
    avoid this? How do you do it with your RNG?
     
    mike3, Jan 9, 2010
    #9
  10. mike3

    James Kanze Guest

    Yes. My idea was to have a template class calling rand() (or
    some other generator), with the max value a template argument.
    More or less. The template has to know the type name, in order
    to declare the return type, and it needs to know the maximum
    value. If enums created a scope, you could simply require that
    the enum define a reserved entry, max, and the maximum value, in
    the template, would be T::max. Because enum's don't introduce
    scope, however, this would mean that you cannot have more than
    one such enum in each namespace. And if the name is T_max, or
    some such (with the name of the type embedded), you can't access
    it in the template.

    The classical solution for this is a traits template class, the
    standard even provides one which you can specialize
    (std::numeric_limits), but it requires defining a lot more than
    you need here.
    I don't. I've never actually needed a random enum, so I'm just
    speculating on solutions. If I did need one... I already have
    code which can parse enums (originally in order to generate a
    mapping between strings and the enum values); it would be
    trivial to add support for rand to it, and generate the required
    classes or whatever automatically. (Note that there can be some
    tricky aspects. When I added generation for iterators, I needed
    a type which would hold "one past the end". Since I only
    support iterators for enums with no user defined values, I just
    used "unsigned"---it seems a reasonable restriction that my code
    won't support an enum with more than UINT_MAX-1 members. I
    suspect that a similar constraint would be acceptable in your
    case.)
     
    James Kanze, Jan 9, 2010
    #10
  11. Am 09.01.2010 14:25, schrieb James Kanze:
    Easiest way would be an enum in a struct. For example:

    /// Don't forget to document this mysterious function
    template <typename Enum>
    typename Enum::type getEnumMax() { return Enum::max; }

    struct Enum1 {
    enum type {
    value1,
    value2,
    max
    };
    };

    In C++0x you can use "enum class" instead.
     
    Thomas J. Gritzan, Jan 9, 2010
    #11
  12. mike3

    James Kanze Guest

    Yes, but if I tell my boss that we can't deliver until Visual
    C++ supports scoped_enum, I don't think it will go over too
    well.
     
    James Kanze, Jan 9, 2010
    #12
  13. mike3

    mike3 Guest

    Hmm. So then what should I do?
    What does this "parse enums" thing do and how does it work?
     
    mike3, Jan 10, 2010
    #13
  14. mike3

    James Kanze Guest

    Whatever seems best to you. There is no perfect solution. I've
    presented some of the alternatives, but it's up to you to choose
    which one seems best for you.
    Originally, it was just a bit of lex and a very simple state
    machine, to determine when an enum started, extract its name and
    members, and then write out the necessary code for mapping
    between the enum values and strings. It then got extended to
    handle namespaces and classes, so that it could be used to map
    nested enums, and to generate other things as well; once you've
    collected the information, there are a lot of things you can
    generate easily.

    Another alternative is to invent a little language to describe
    your enums, and generate the enum declaration and any additional
    code you want from that. That's easier even than parsing; it
    tends to work best when the enums are in global scope (but you
    can add options for it to generate e.g. namespaces, and if you
    prefer your enums in classes, you can do that as well). For the
    simplest case, you can even use a small language, like AWK, for
    the generator. (This is doubtlessly the route I would have
    taken, rather than my parser, if I hadn't had to deal with
    legacy code which used enum, and not any little language I could
    invent. And the fact that my enums were often members of a
    larger class.)
     
    James Kanze, Jan 10, 2010
    #14
  15. mike3

    mike3 Guest

    I suppose.
    Mmm. This seems to be highly complicated, far too much for what
    is otherwise a simple problem, even simpler than converting to
    strings.
     
    mike3, Jan 10, 2010
    #15
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.