0

I am trying to implement enum class for data types similar to Java enums.

DataType.h:

namespace Galactose {
    class DataType {
    public:
        DataType(const std::string& a_name, const size_t a_byteSize);
        DataType(const std::string& a_name, const DataType& a_componentType, const uint32_t a_componentCount);

    private:
        inline static std::vector<DataType> s_types;

        int32_t m_ordinal;
        std::string m_name;
        size_t m_byteSize;
        const DataType& m_componentType;
        uint32_t m_componentCount;
    };

    namespace DataTypes {
        inline static const DataType FLOAT(GT_STRINGIFY(FLOAT), sizeof(float));
        inline static const DataType VECTOR2(GT_STRINGIFY(VECTOR2), FLOAT, 2);
        inline static const DataType VECTOR3(GT_STRINGIFY(VECTOR3), FLOAT, 3);
        inline static const DataType VECTOR4(GT_STRINGIFY(VECTOR4), FLOAT, 4);
    };
}

DataType.cpp:

#include "DataType.h"

namespace Galactose {
    DataType::DataType(const std::string& a_name, const size_t a_byteSize)
        : m_ordinal(int32_t(s_types.size())),
          m_name(a_name),
          m_byteSize(a_byteSize),
          m_componentType(*this),
          m_componentCount(1)
    {
        s_types.emplace_back(*this);
    }

    DataType::DataType(const std::string& a_name, const DataType& a_componentType, const uint32_t a_componentCount)
        : m_ordinal(int32_t(s_types.size())),
          m_name(a_name),
          m_byteSize(a_componentType.m_byteSize * a_componentCount),
          m_componentType(a_componentType),
          m_componentCount(a_componentCount)
    {
        s_types.emplace_back(*this);
    }
}

GT_STRINGIFY defined in a precompiled header like this:

#define GT_STRINGIFY(x) #x

Problem is variables in DataTypes are instantiated multiple times. Probably once per #include "DataType.h". I want them to be instantiated only once.

I tried to change DataTypes namespace by class but gives Error C2059 syntax error: 'string' at each GT_STRINGIFY.

I know I can check whether name already exist, or use set instead of vector. But I don't really want to go down this road.

Thank you for your time. Any suggestions are welcomed.

Kao
  • 537
  • 5
  • 12

2 Answers2

2

Using another languages tools/techniques is generally not the way forward, so to you I would recommend an enum class (which is C++ specific and is probably the closest built in thing you will get). Why is enum class preferred over plain enum?

Also it should be noted you need header guards: Header guards in C++ and C basically this:

#ifndef HEADER_NAME_HPP
#define HEADER_NAME_HPP
//code
#endif
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • I was thinking the same, each language has its own way of doing things. And trying to translate from one language to another usually only leads to confusion. C++ much more then java is suited for checking things at compile time and you should use that to your advantage – Pepijn Kramer Sep 02 '21 at 07:48
  • Header has `#pragma once` for header guard. Thank you for your time. – Kao Sep 02 '21 at 15:19
  • that's MSVC specific, so if you migrate to another compiler it may not work – Thomas Bourne Sep 02 '21 at 16:49
1

static on a variable in namespace scope means this variable exists once per translation unit (.cpp file) using this header. The way of making sure all translation units share the same object would be to declare these variables it as extern and define them in DataType.cpp.

DataType.h:

...
    namespace DataTypes {
        extern const DataType FLOAT;
        extern const DataType VECTOR2;
        extern const DataType VECTOR3;
        extern const DataType VECTOR4;
    };
...

DataType.cpp:

...

    namespace DataTypes {
        const DataType FLOAT("FLOAT", sizeof(float));
        const DataType VECTOR2("VECTOR2", FLOAT, 2);
        const DataType VECTOR3("VECTOR3", FLOAT, 3);
        const DataType VECTOR4("VECTOR4", FLOAT, 4);
    };
...

To get even closer to a java enum you may want to declare the symbols as static variables in DataType instead allowing you to refer to them via e.g. Galactose::DataType::FLOAT.

...
class DataType {
    public:
        DataType(const std::string& a_name, const size_t a_byteSize);
        DataType(const std::string& a_name, const DataType& a_componentType, const uint32_t a_componentCount);

        static const DataType FLOAT;
        static const DataType VECTOR2;
        static const DataType VECTOR3;
        static const DataType VECTOR4;

    private:
        ...
    };
...

DataType.cpp:

...
    const DataType DataType::FLOAT("FLOAT", sizeof(float));
    const DataType DataType::VECTOR2("VECTOR2", FLOAT, 2);
    const DataType DataType::VECTOR3("VECTOR3", FLOAT, 3);
    const DataType DataType::VECTOR4("VECTOR4", FLOAT, 4);
...

You could of course use a separate class containing the static members, but this would be different to java where the enum constants are accessible via the enum type.


I do assume in this answer that your header file wasn't posted completely. Otherwise you should add header guards to avoid problems when including this file indirectly via multiple other headers; furthermore you should add includes for all the types used. The following includes would be missing from the header:

#include <string>
#include <vector>
fabian
  • 80,457
  • 12
  • 86
  • 114
  • I put instances in a different namespace. Because I didn't want to separate definition and initialization of `DataType`s. But I guess there is no other way of doing it. `string` and `vector` included in precompiled header btw. Thanks for pointing that out. – Kao Sep 02 '21 at 15:22