3

I have the following code that not only defines a scoped enumeration, but also has a corresponding mapping with it so I can use the stringized version of its enumerators directly with iostream operator overloads.

enum class ArbitraryNumbers
{
    One,
    Two,
    Three,
    Four,
    Five
};

namespace
{
    using bm_type = boost::bimap<ArbitraryNumbers, std::string>;
    bm_type const g_arbitraryNumbersMap = boost::assign::list_of<bm_type::relation>
        (ArbitraryNumbers::One, "One")
        (ArbitraryNumbers::Two, "Two")
        (ArbitraryNumbers::Three, "Three")
        (ArbitraryNumbers::Four, "Four")
        (ArbitraryNumbers::Five, "Five")
        ;
}

std::ostream& operator<< (std::ostream& os, ArbitraryNumbers number)
{
    auto it = g_arbitraryNumbersMap.left.find(status);
    if (it != g_arbitraryNumbersMap.left.end())
    {
        os << it->second;
    }
    else
    {
        os.setstate(std::ios_base::failbit);
    }

    return os;
}

std::istream& operator>> (std::istream& is, ArbitraryNumbers& number)
{
    std::string number_string;
    is >> number_string;
    auto it = g_arbitraryNumbersMap.right.find(number_string);
    if (it != g_arbitraryNumbersMap.right.end())
    {
        status = it->second;
    }
    else
    {
        is.setstate(std::ios_base::failbit);
    }

    return is;
}

I'd like to wrap this in some sort of reusable fashion. I think the best solution will involve some macros, ideally something like this:

BEGIN_SCOPED_ENUM(ArbitraryNumbers)
    ENUMERATOR(One)
    ENUMERATOR(Two)
    ENUMERATOR(Three)
    ENUMERATOR(Four)
    ENUMERATOR(Five)
END_SCOPED_ENUM()

This is very MFC-like, which is mostly an immediate turn-off for me. But at least this is MUCH easier on the eyes. Another point is that it eliminates boilerplate and is less error prone since the mapping doesn't need to be kept in sync with additions or removals to and from the enumeration itself.

Is there a way I can accomplish this with Boost.Preprocessor, template trickery, or some other useful mechanic?

I did find some ideas online but most of it was hand-spun macros, which I want to avoid. I feel like if I have to go the macro approach that Boost.Preprocessor will be able to do this for me, but it's very complex to use and I'm not sure how to use it to solve this problem. Also most of the solutions I found were really dated, and I want to see if answers would be different with features introduced in STL and core language since between C++03 and C++14.

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • do th earbitrary number have to follow one another? or they can have different interval in between? – David Haim Sep 25 '15 at 22:14
  • This is probably doable with X macros. – Daniel Jour Sep 25 '15 at 23:22
  • Take a look at this: https://en.wikipedia.org/wiki/X_Macro – peje Sep 28 '15 at 10:31
  • It seems that x macros assume a fixed number of repetitions. I won't always have that same number of enumerators in each enumeration. – void.pointer Sep 28 '15 at 12:55
  • Try this library, which uses some combination of macros and template tricks: https://github.com/aantron/better-enums (disclaimer: I wrote it). The internal workings are sketched out in this answer: http://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c-and-future-c17/31362042#31362042. I think this is, technically, a duplicate question – does the linked question cover your needs? – antron Oct 01 '15 at 21:40

1 Answers1

-2

i'd just write an array:

std::string array[] = {"one", "two", "three"};

I guess the issue is the same as with generating sin-tables automatically.

tp1
  • 1,197
  • 10
  • 17
  • Could you explain how this is related to generating _sin-tables_ [sic]? – sehe Sep 25 '15 at 23:21
  • @sehe: yes, generally c++ doesnt support generating large tables automatically, you'd need to do a for-loop for that, but that's not an alternative if you want a static table like sin-table. Basically you need code generation to get a table like that done. – tp1 Sep 25 '15 at 23:58
  • That seems unrelated to me. After all, there is no function (like sin) to call to get the name of an enum member as a string. So you couldn't "need to do a for-loop for that" in the first place. – sehe Sep 25 '15 at 23:59
  • @sehe: how its related is that you can't fill several items of an array with a single expression. sin table generation has that problem. And his boilerplate problem has the same problem. – tp1 Sep 26 '15 at 00:05
  • This doesn't address my question at all. – void.pointer Sep 26 '15 at 13:50