johnco3
johnco3

Reputation: 2652

Defining static constexpr values in an enum like wrapper class

I have a class that I regularly use for an enum wrapper, however this requires a cpp file. Can anyone show me how to use constexpr

The header file is as follows:

// Extend-able in the future
class CDUVariable {
public:
    enum Value {
        baud, firmware, type, serial
    };
    static const CDUVariable Baud, Firmware, Type, Serial;

    /// Comparison operator (used for strict weak ordering).
    bool operator<(const CDUVariable& rhs) const {
        return mValue < rhs.mValue;
    }

    /// Integral operator cast for switch statements (cast to named enum).
    operator const Value() const {
        return mValue;
    }

    /// Serialized version of the enum.
    std::string getStringVal() const {
        return mStringVal;
    }
    static const std::set<CDUVariable>& getValues() {
        static std::set<CDUVariable> gValues;
        if (gValues.empty()) {
            gValues.insert(Baud);
            gValues.insert(Firmware);
            gValues.insert(Type);
            gValues.insert(Serial);
        }
        return gValues;
    }
    static CDUVariable valueOf(const std::string& rStringVal) {
        for (const auto& next : getValues()) {
            if (next.getStringVal() == rStringVal) {
                return next;
            }
        }
        throw std::invalid_argument(
            "Illegal Argument: " + rStringVal);
    }
private:
    CDUVariable(const Value& rValue, const std::string& rStringVal)
        : mValue(rValue)
        , mStringVal(rStringVal)
    {}
    Value mValue;
    std::string mStringVal;
};

and the cpp file is:

const CDUVariable CDUVariable::Baud(baud, "0");
const CDUVariable CDUVariable::Firmware(firmware, "1");
const CDUVariable CDUVariable::Type(type, "2");
const CDUVariable CDUVariable::Serial(serial, "3");

I would like to know if it is possible to use the new constexpr syntax to initialize everything in the header file. I am having trouble with the syntax.

I tried prefixing modifying the header as follows:

// Extend-able in the future
class CDUVariable {
public:
    constexpr enum Value {
        baud, firmware, type, serial
    };
    constexpr static CDUVariable Baud(baud, "0") Firmware(firmware, "1"), Type(type, "2"), Serial(serial, "3");

But this ended up giving me a bunch of errors. I would like to know how to migrate to the new constexpr syntax and the plus side of this would be that I could ship a header only library containing these enum type classes.

2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C2061: syntax error: identifier 'baud'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C3646: 'Firmware': unknown override specifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C2059: syntax error: '('
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2143: syntax error: missing ')' before ';'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2098: unexpected token after data member 'mValue'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2059: syntax error: ')'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(111): error C2065: 'mValue': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(111): error C2039: 'mValue': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(116): error C2065: 'mValue': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(126): error C3867: 'CDUVariable::Baud': non-standard syntax; use '&' to create a pointer to member
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(127): error C2065: 'Firmware': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(128): error C2065: 'Type': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(129): error C2065: 'Serial': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(146): error C2614: 'CDUVariable': illegal member initialization: 'mValue' is not a base or member
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): error C2350: 'CDUVariable::Baud' is not a static member
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): note: see declaration of 'CDUVariable::Baud'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): error C2248: 'CDUVariable::CDUVariable': cannot access private member declared in class 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(143): note: see declaration of 'CDUVariable::CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(166): error C2039: 'Firmware': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(166): error C2065: 'firmware': undeclared identifier
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(167): error C2039: 'Type': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(167): error C2065: 'type': undeclared identifier
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(168): error C2039: 'Serial': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(168): error C2065: 'serial': undeclared identifier
2>Generating Code...

Upvotes: 1

Views: 1713

Answers (3)

skypjack
skypjack

Reputation: 50550

I have a class that I regularly use for an enum wrapper, however this requires a cpp file. Can anyone show me how to use constexpr

To reduce your example to a minimal one, consider the following class:

struct Example {
    enum Value { A, B };
    static constexpr Example Foo{Value::A, "bar" };
    constexpr Example( Value v, std::string s): v{v}, s{s} {}

private:
    Value v;
    std::string s;
};

This is more or less a reduced version of your last attempt.
Almost all the pieces required to be able to construct a constexpr version of Example are in place, but for the fact that:

  • static constexpr data members must be initialized, but Example is an incomplete type at the point of declaration of Foo.

  • All the data members must be (let me say) constexpr constructible and std::string hasn't a constexpr constructor you can use.

Therefore your attempt will fail, no doubts about that.

Moreover, note that you cannot odr-use constexpr data members unless you are using C++17 or you define them separately in a .cpp file, exactly as you did until now. That's because those members are not implicitly defined in C++11/14.
It means that you cannot even use them with std::set::insert for it accepts only references. At least, you cannot use them unless you put a definition in a .cpp file, of course.

So, back to the question:

Can anyone show me how to use constexpr

No, we cannot and it doesn't worth it. It seems to me that you want odr-usable data members, so go on with static ones and put definitions in a dedicated file.

Upvotes: 2

Rakete1111
Rakete1111

Reputation: 48958

By definition, an enum values are constant, after all, you can't add values at runtime. That's why one reason you are not allowed to put constexpr before an enum declaration, it would make no sense, as it wouldn't change anything about the enum:

constexpr enum Value { baud, firmware, type, serial };
^^^^^^^^^
 illegal

As a side note, you cannot construct a constexpr CDUVariable right now because its constructor is not constexpr. If you try to mark it appropriately, it will fail, because mStringVal doesn't have a constexpr constructor and every variable has to be initialized.

If you are only going to store single characters, use a char, which as a primitive, is able to be constructed at compile time.

Upvotes: 2

Jarod42
Jarod42

Reputation: 217293

You may get rid of those static variables with:

static const std::set<CDUVariable>& getValues() {
    static const std::set<CDUVariable> gValues = {
        { baud, "0"}, // or Baud(),
        { firmware, "1"},
        { type, "2"},
        { serial, "3"}
    };
    return gValues;
}

and

static const CDUVariable& Baud() { static const CDUVariable var{ baud, "0"}; return var; }

or

static CDUVariable Baud() { return { baud, "0"}; }

Upvotes: 1

Related Questions