0

Here's a design problem that I haven't been able to find a satisfying solution for. Title reflects what I think I want, but I'm definitely open to suggestions.

I have a struct that holds some data about a command. Sort of like this:

enum class CommandType {
    COMMAND1,
    COMMAND2
};

struct DataStruct {
    CommandType commandType;
    std::string someParam;
    int someData;
///etc
};

I have a base class that parses chunks of data and populates the struct, and defines some virtual functions that do some operations on based on the parsed commandType and params.

class Base : public QObject
{
    Q_OBJECT
public:
    Base() {}
    virtual ~Base() {}
    
    virtual void parse(/* some bytes */){
        DataStruct s;
        /* populate struct fields */
        /* do common interface stuff with s */
        Func(s); //do subclass specific stuff with s
    }

    virtual void Func(DataStruct s) = 0;
};

class Derived : public Base
{
    Q_OBJECT
public:
    Derived() : Base() {}
    virtual ~Derived() {}
    
    virtual void Func(DataStruct s) override {
        switch(s.commandType){
        case CommandType::COMMAND1:
            cout << "Here";
            break;
        case CommandType::COMMAND2:
            cout << "There";
            break;
        }
        
    }
};

The unsatisfying part is, the different subclasses implement the same core set of commands, but there are also some commands that are specific to the subclass type. My current approach requires me to put all of these specific commands into the same enum as the core commands, which works but I would prefer the base class to not have to change every time a new command is added to a subclass or have any "knowledge" of subclass specific functionality at all ideally.

My original half-baked idea was to have each subclass have its own enum of commands, move the commandType parsing into the subclasses so that they can choose a value from their appropriate enum, and make the commandType templated, but this doesn't work because virtual functions can't be templated and QObjects can't be templated classes. I didn't show them in my example, but both the base and derived classes use meta objects and signals/slots.

I also do not want to use boost if possible. Would this be the type of situation to use a QVariant for storing a value from an arbitrary enum? If so, how would I safely convert back to the correct enum + value that I started with?

Cobalt
  • 938
  • 9
  • 21
  • Essentially, by choosing to use an `enum class` you have prevented doing what you want, since inheritance is not possible from `enum`s. Instead of using an `enum class` use named `const int`s. That allows your derived classes to specify their own set of `const int`s that extend from the inherited set and for their `parse()` and `Func()` functions to do what is required with those. For more information see https://stackoverflow.com/questions/644629/base-enum-class-inheritance and https://stackoverflow.com/questions/42913431/enums-support-for-inheritance – Peter Jul 24 '20 at 00:15
  • Defining `enum CommandType` suggests that you *do* want to see all commands in one place. Maybe you should elaborate some more on why, and also why prefer a strongly typed `enum` to begin with. – dxiv Jul 24 '20 at 00:35
  • @dxiv I wanted an enum basically so the compiler would nag me if I forgot to handle the case somewhere. And of course when I started all of the subclasses at the time had the same command set and then naturally a couple years later that's no longer true – Cobalt Jul 24 '20 at 15:18
  • @Peter I know I can't inherit enums. I think what I want to do is store the `commandType` as an int, and in the subclasses convert to a value of the correct enum. But I was wondering if there was a better way I didn't anticipate, or if I could use something like a `QVariant` to convert back to the correct enum type more safely – Cobalt Jul 24 '20 at 15:22

0 Answers0