Search code examples
c++enumsnamespacescompiler-constructioncode-generation

Is there a way to nest enums in c++ without namespace?


I'm implementing a byte code generator for a given AST. While creating the expression class I noticed the distincition between unary and binary operators. For simplicity unum was the go-to solution, so I nested the enuns this way:

enum EXP_TYPE
{
    enum BINARY
    {
        PLUS,
        MINUS,
        MULTIPLY,
        DIV,
        ...
    };
    enum UNARY
    {
        PLUS,
        MINUS,
        POINTER,
        INC,
        ...
    };
};

Of course this code throws warnings informing this is not allowed. So I searched a bit on stackoverflow and other sources, and found the solution of using namespace instead of enum for EXP_TYPE declaration. But the problem now is the use inside the expression class:

class expression
{
    expression *right_exp;
    expression *left_exp;
    EXP_TYPE type;
    ...
};

The use of EXP_TYPE is not allowed anymore due to it being a namespace instead of a enum type. What I intented to make was a generic enum declaration that could be used as a atribute inside a class, in here generic means that it could be BINARY or UNARY, that is, the atribute EXP_TYPE type could have assignments and comparisons like:

 expression exp1;
 exp1.type = EXP_TYPE::BINARY::PLUS;
 exp1.type = EXP_TYPE::UNARY::PLUS;

Is there a way to make these simple generic types without using namespace in the way presented, or without the need of creating a class hierarchy for unary and binary operators ?


Solution

  • I took this as puzzle to find out how close I could come to what the OP requested.

    This is what I got (though I must admit it looks a bit scaring):

    #include <cassert>
    #include <iostream>
    
    struct ExpType {
      struct Unary {
        enum {
          Plus, Minus, Pointer, Inc,
          N
        };
      };
      struct Binary {
        enum {
          Plus = Unary::N, Minus, Multiply, Div,
          N
        };
      };
      enum {
        N = Binary::N
      };
    
      int value;
    
      ExpType(int value = 0): value((assert(value >= 0 && value < N), value)) { }
      ~ExpType() = default;
      ExpType(const ExpType&) = default;
      ExpType& operator=(const ExpType&) = default;
    
      operator int () { return value; }
    };
    
    int main()
    {
      for (int i = 0; i < ExpType::N; ++i) {
        ExpType expType = i;
        switch (expType) {
    #define CASE(TYPE) case TYPE: std::cout << #TYPE "\n"; break
          CASE(ExpType::Unary::Plus);
          CASE(ExpType::Unary::Minus);
          CASE(ExpType::Unary::Pointer);
          CASE(ExpType::Unary::Inc);
          CASE(ExpType::Binary::Plus);
          CASE(ExpType::Binary::Minus);
          CASE(ExpType::Binary::Multiply);
          CASE(ExpType::Binary::Div);
    #undef CASE
          default: std::cout << "Unknown expression type!\n";
        }
      }
    }
    

    Output:

    ExpType::Unary::Plus
    ExpType::Unary::Minus
    ExpType::Unary::Pointer
    ExpType::Unary::Inc
    ExpType::Binary::Plus
    ExpType::Binary::Minus
    ExpType::Binary::Multiply
    ExpType::Binary::Div
    

    Live Demo on coliru

    However, I must admit I personally would surely prefer what was recommended in the other answer.