extensible enum class?

Hi

I'm working on a larger project at the moment, for which I am developing a class (call it X class and X objects). This class currently allows the user to specify one or more enums which I've defined in the same headerfile as where X class is defined. All of this looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

enum class UType {
   TYPE_A,
   TYPE_B,
   TYPE_C,
};

class X 
{

private:

UType x_enum;

//lots of other stuff

public:

void setEnum(UType);

//lots of other stuff

}


The problem with this is that UType is predefined by me, whereas I'd like the client/user to specific their own enums. This is not something that enums support, you can't add new enums later (I think this means they are not 'extensible').

I've been thinking about how bets to address this. I suppose you could just let the user supply a string but that seems a bit unconstrained. So I've come up with the following (very much some quick code which I haven't properly tested):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

typedef string UType;

class expEnum
{
    private:
        map<UType, int> enum_register;
        
        int findLargestEnum ()
        {
            int highest_enum = 0;
            for (map<UType, int>::iterator it = enum_register.begin(); it != enum_register.end(); ++it)
            {
                if ((it -> second) > highest_enum)
                {
                    highest_enum = it -> second;
                }
            }
            return highest_enum;
        }

    
    public:
        bool addEnum(UType new_enum)
        {
            if (enum_register.count(new_enum) == 0) //enum doesnt exist
            {
                int highest_enum = findLargestEnum(); //find highest existing enum
                enum_register.insert(pair<UType,int>(new_enum, highest_enum + 1));
                return true; 
            }
            return false; //enum already exists
        }
        
        bool hasEnum (UType input)
        {
            return enum_register.count(input);
        }
        
        int enumCount ()
        {
            return enum_register.size();
        }


X would still contain a UType variable but this would effectively be a string rather than a true enum. Users could add new enums to an expEnum object (which would presumably sit in X as a variable) by supplying a new string.When users wanted to set an enum in X they would supply a UType variable which would be checked against expEnum (to see if it exists).

Any thoughts gratefully received (either on the above code or whether there is a better way of achieving the desired functionality).

Thanks
Last edited on
Indeed, the "enum" is a compile time notion, so it naturally isn't modifiable at runtime.

What you've proposed is a rather common design pattern, that of user defined (or otherwise runtime defined) codes and values.

if utype is built in types, you are making something that may end up doing a bunch of unnecessary work to store, what is effectively, just a numeric constant.

If utype is even classes that can be resolved to built in types, this is still true.

if it gets more complicated, you are on the right path.

to see that more clearly... say utype is just 'double'. Does your class resolve to compile time subs of name=value in the assembly language, or does it run through a bunch of memory smoke and mirrors to get there?

Last edited on
> I'd like the client/user to specific their own enums.

If the class X can be a header-only class, consider making it a class template.
For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <string>

template < typename UType > struct X
{
    X( UType type, double value, std::string type_name, std::string label )
        : type(type), value(value), type_name( std::move(type_name) ), label( std::move(label) ) {}

    UType type ;
    double value ;
    std::string type_name ;
    std::string label ;

    friend std::ostream& operator<< ( std::ostream& stm, const X& x )
    {
        return stm << "X{ " << x.type_name << '(' << (long long)x.type << "), "
                   << x.value << ", " << x.label << " }" ;
    }
};

int main()
{
    enum distance { CMS, MTS, KMS };
    const X diameter{ CMS, 12.6, "cms", "diameter" } ;
    std::cout << diameter << '\n' ;

    enum class storage : long long { KB = 1024, MB = KB*1024, GB = MB*1024, TB = GB*1024 };
    const X disk_space { storage::GB, 1234.5, "GB", "disk space" } ;
    std::cout << disk_space << '\n' ;
}

http://coliru.stacked-crooked.com/a/36b72dd27f035043
Topic archived. No new replies allowed.