I’m working on a message parser/generator subsystem. I’m creating an auto-generator that uses a database that contains all of the information about this protocol, including enum lists, to generate the code. One thing I came across is the need for hierarchical enumerations.
updated
(I was trying to simplify things by not describing the full problem, but the comments below make it obvious that I erred by simplifying too much.)
The Database being used will store things as simplified strings (customer decision), but the protocol only speaks “byte triplets” (aka Hierarchical Enum). The full problem could be described as such:
Given a set of unique strings that each correspond with a unique triplet, 1) find the triplet for any given string, and 2) find the string for any given triplet. Make sure to account for “Undefined” and “No Statement” enumerations (which do not have strings associated with them). [As one poster noted, yes it is insane.]
(Caveat: I’ve been doing C++ for well over a decade, but I’ve been doing Java this last year — my C++ is probably “corrupted”.)
So, to use an admittedly contrived example, given:
// There is only one category
// POP= "P", COUNTRY= "K", CLASSICAL= "C"
enum Category {POP, COUNTRY, CLASSICAL};
// There is one Type enum for each Category.
// ROCK= "R", BIG_BAND = "B", COUNTRY_POP= "C"
enum PopType {ROCK, BIG_BAND, COUNTRY_POP};
enum CountryType {CLASSICAL_COUNTRY, MODERN_COUNTRY, BLUEGRASS, COUNTRY_AND_WESTERN};
// ...
// There is one Subtype for each Type
// EIGHTIES= "E", HEAVY_METAL= "H", SOFT_ROCK= "S"
enum RockSubType { EIGHTIES, HEAVY_METAL, SOFT_ROCK};
// ...
When I get 0, 0, 0 (Pop, Rock, Eighties), I need to translate that to “PRE”. Conversely, if I see “PC” in the Database, that needs to be sent out the wire as 0, 2 (Pop, Country, NULL).
I’m blatantly ignoring “Undefined” and No Statement” at this point. Generating a triplet from a string seems straight forward (use an unordered map, string to triple). Generating a string from a triplet (that may contain a NULL in the last entry) … not so much. Most of the “enum tricks” that I know won’t work: for instance, Types repeat values — each Type enum starts at zero — so I can’t index an array based on the Enum value to grab the string.
What’s got me is the relationship. At first glance it appears to be a fairly straight forward “is-a” relationship, but that doesn’t work because this case is bidirectional. The leaf -> root navigation is very straight forward, and would be appropriate for a class hierarchy; unfortunately, going the other way is not so straight forward.
I cannot “hand roll” this — I have to generate the code — so that probably eliminates any XML based solutions. It also has to be “reasonably fast”. The “Java Solution” involves using protected static variables, initialized on construction, and abstract base classes; however, I do not believe this would work in C++ (order of initialization, etc.). Plus, aesthetically, I feel this should be … more “const”. Other code I’ve seen that tackles this problem uses unions, explicitly listing all of the enum types in the union.
The only other thing I can come up with is using Template Specialization and explicit specialization, but I’m at a loss. I did a web search on this, but I found nothing that would tell me if it would even work. Still, if it can be done with a union, can’t it be done with Template Specialization?
Is it possible to do something like this using templates, specialization, explicit specialization? Is there another, more obvious, solution (i.e. a design pattern that I’ve forgotten) that I’m missing?
Oh, before I forget — the solution must be portable. More specifically, it must work on Windows (Visual Studio 2010) and Redhat Enterprise 6/Centos 6 (GCC 4.4.4 IIRC).
And, lest I forget, this protocol is huge. The theoretical max on this is about 133,000 entries; once I include “Undefined” and “No Statement” I’ll probably have that many entries.
Thanks.
First, thanks to everyone for their help. I wasn’t actually able to use any of the answers “as is” because of the nature of this problem:
I eventually found Boost bimaps and it turns out that a
bimaphierarchy works well for this problem. For those that haven’t seen them, Boost `bimap’ is a bidirectional container that uses either of the pair as key and the other as value.I can make a
bimapof “integer, string” (uint8_t in this case, since the enums here are all guaranteed to be small) and add the, errr, “sub-enum”, as information associated with thebimapusingwith_info.The hierarchy code looks something like this:
I chose
unordered_setfor performance reasons. Since this is strictly a “constant” hierarchy, I don’t have to worry about insertion and deletion times. And because I’ll never be comparing order, I don’t have to worry about sorting.To get the Category information by enum value (get string values when given the enum), I use the
category_enum_valuetag:I get the appropriate Type information from this by doing this, using the
type_enum_valuetag (subtype is nearly identical):To get the enum values given the string, change the tag to
category_stringand use similar methods as before:Any additional information that I need for any given level (say, menu item strings) can be added by changing the info type from a
bimapto astructcontaining abimapand whatever information I might need.Since this is all constant values, I can do all the hard work “up front” and design simple look-up functions — O(1) — to get what I need.