C++ enum problem in use

Asked 2 years ago, Updated 2 years ago, 26 views

#include <iostream>

struct Tag
{

    enum class A {
        testA
    };

    enum class B {
        testB
    };
};

class Test {

    Tag m_Tag;

    Test(Tag tag) : m_Tag(tag) {}
};

int main()
{

        Test* test = new Test(Tag::A::testA);

}

It's a code that I made because I wanted to have one of the two variables of m_Tag instead of having two A and B Tag in the Test class It doesn't work well even if I convert the type casting to compile the above code successfully, is there any way?

Or is there a way to make it into a different structure?

c++

2022-09-22 08:36

2 Answers

Hello, I thought it was an interesting idea, so I implemented itHaha I don't know if it's the right direction, but... It can be implemented using a template.

But in fact, I don't think this kind of structure is going to be very useful. This is because the purpose of the enum class itself is for enumerations that clearly know the type at compile time.

Once you omit all exceptions and implement them simply, you can implement them as follows.

#include <iostream>

struct Tag
{
    enum class A {
        testA1,
        testA2
    };
    enum class B {
        testB
    };

    template<typename T> T Get() const {
        return static_cast<T>(m_Value);
    }
    template<typename T> static Tag Set(T value) {
        Tag ret;
        ret.m_Value = static_cast<int>(value);
        return ret;
    }
private:
    int m_Value;
};

class Test
{
public:
    Test(Tag tag) : m_Tag(tag) {}

    template<typename T> T GetTag() {
        return m_Tag.Get<T>();
    }
private:
    Tag m_Tag;
};


int main()
{
    Test* test = new Test(Tag::Set(Tag::A::testA2));

    // The stored value is loaded properly and "tastA2" is output.
    Tag::A tagA = test->GetTag<Tag::A>();
    switch (tagA) {
    case Tag::A::testA1: std::cout << "tastA1" << std::endl; break;
    case Tag::A::testA2: std::cout << "tastA2" << std::endl; break;
    }

    delete test;
    return 0;
}

If you don't understand the template at all, I think you can easily understand the code above without explaining it separately.

However, since the above code is actually too unsafe to write (because it allows a wrong Get call), we have enhanced safety and implemented further.

The code below contains C++17 grammar, so it can only be compiled in Visual Studio 2017.

#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <type_traits>

struct Tag
{
    enum class A {
        testA1,
        testA2
    };
    enum class B {
        testB
    };

    template<typename T, typename = std::enable_if_t<
        std::is_same_v<T, A> ||
        std::is_same_v<T, B>>>
    T Get() const
    {
        // Using template meta-programming and RTTI
        // You can ensure the safety of the type.
        if (std::type_index(typeid(T)) != m_Type)
        {
            // It's safer to make exceptions through throw,
            // If exception handling is implemented, there will be too much to explain, so we will skip it.
            std::cout
                <<"Warning: Import in a different type than when saved." << std::endl
                <<" - Saved Type:" <<< m_Type.name() << std::endl
                <<" - Type to import:" << typeid(T).name() << std::endl;
        }
        return static_cast<T>(m_Value);
    }
    template<typename T, typename = std::enable_if_t<
        std::is_same_v<T, A> ||
        std::is_same_v<T, B>>>
    static Tag Set(T value)
    {
        // Using a factory pattern,
        // The Tag class itself is not a template, but is associated with a template
        // Lets you create an instance.
        Tag ret;
        ret.m_Type = typeid(T);
        ret.m_Value = static_cast<int>(value);
        return ret;
    }
private:
    std::type_index m_Type  = typeid(m_Value);
    int             m_Value = 0;
};


class Test
{
public:
    Test(Tag tag) : m_Tag(tag) {}

    template<typename T>
    T GetTag()
    {
        return m_Tag.Get<T>();
    }
private:
    Tag m_Tag;
};


int main()
{
    Test* test1 = new Test(Tag::Set(Tag::A::testA2));

    // The stored value is loaded properly and "tastA2" is output.
    Tag::A tagA = test1->GetTag<Tag::A>();
    switch (tagA)
    {
    case Tag::A::testA1:
        std::cout << "tastA1" << std::endl;
        break;
    case Tag::A::testA2:
        std::cout << "tastA2" << std::endl;
        break;
    }

    // If you try to import it into a type other than a saved type,
    // A warning message is output.
    Test* test2 = new Test(Tag::Set(Tag::B::testB));
    tagA = test2->GetTag<Tag::A>();

    delete test1;
    delete test2;
    return 0;
}


2022-09-22 08:36

Thank you for all the information. I'm not used to C++17, so there are many new keywords. But as time went by, the results that I thought about didn't seem to be a good way to use enumeration, so I ended up using it in a different way. It works well with the method you wrote, but it's better not to use that method


2022-09-22 08:36

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.