确保枚举对应于某个模板类型

Ensure that enum corresponds to a certain template type

本文关键字:类型 于某个 枚举 确保      更新时间:2023-10-16

我正在尝试将数据类型从文件映射到内存,所以我有一个类,它为每一列获取信息。不同的类型有不同的映射参数,所以我为每个支持的类型创建了一个具有适当参数的结构。我设法通过这种方式解决了类型安全性的问题,现在我使用了一个模板,即数字类型,它们共享相同的属性,只有类型不同。唯一仍然相当丑陋的是,我不能仅仅基于支持的类型来分配正确的枚举。

如下面的示例所示,在数字类型的情况下,我必须手动分配相关类型(示例代码中的c5c6)。现在我想知道是否有一个更优雅的解决方案,这样我就可以使用一些模板技术(enable_if?)来仅根据支持的类型选择正确的枚举值(c7是预期的目标示例)。

我使用的是Visual Studio 2010,它支持C+11的一个子集。

#define _CRT_SECURE_NO_WARNINGS
#include <time.h>
#include <type_traits>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <set>
typedef char byte_t;
typedef unsigned char ubyte_t;
typedef short word_t;
typedef unsigned short uword_t;
typedef unsigned short date_t;
typedef char string_t;
class ColumnDef
{
public:
    typedef enum
    {
        T_UNDEFINED,
        T_byte,
        T_ubyte,
        T_word,
        T_uword,
        T_size_t,
        T_string,
        T_std_string,
        T_date_string,
        T_date,
        T_MAX
    } ColumnDefType;
    typedef enum
    {
        DF_UNDEFINED,
        DF_TTMMYY,
        DF_YYMMTT,
        DF_TTMMYYYY,
        DF_YYYYMMTT,
        DF_TT_MM_YYYY,
        DF_YYYY_MM_TT,
        DF_MAX
    } DateFormat;
    class cstring_type
    {
    public:
        ColumnDefType Type;
        const char *Adress;
        size_t MaxLen;
        static cstring_type init(const char *p, size_t nMaxLen) { cstring_type t = {T_string, p, nMaxLen}; return t; };
    };
    class cpp_string_type
    {
    public:
        ColumnDefType Type;
        std::string *Adress;
        static cpp_string_type init(std::string *p) { cpp_string_type t = {T_std_string, p}; return t; };
    };
    class ubyte_type
    {
    public:
        ColumnDefType Type;
        ubyte_t *Adress;
        ubyte_t Default;
        static ubyte_type init(ubyte_t *p, ubyte_t nDef) { ubyte_type t = {T_ubyte, p, nDef}; return t; };
    };
    class date_type
    {
    public:
        ColumnDefType Type;
        date_t *Adress;
        date_t Default;
        DateFormat Format;
        static date_type init(date_t *p, date_t nDef, DateFormat fmt) { date_type t = {T_date, p, nDef, fmt}; return t; };
    };
    template <typename T, ColumnDefType E>
    class numeric
    {
    public:
        ColumnDefType Type;
        T *Adress;
        T Default;
        static numeric<T, E> init(T *p, T nDef) { numeric<T, E> t = {E, p, nDef}; return t; };
    };
public:
    ColumnDef(void) { mType = T_UNDEFINED; }
    ColumnDef(ubyte_type const &t) { mType = t.Type; ub = t; }
    ColumnDef(date_type const &t) { mType = t.Type; d = t; }
    ColumnDef(cpp_string_type const &t) { mType = t.Type; cps = t; }
    ColumnDef(cstring_type const &t) { mType = t.Type; cs = t; }
    ColumnDef(numeric<size_t, T_size_t> const &t) { mType = t.Type; st = t; }
    ColumnDef(numeric<byte_t, T_byte> const &t) { mType = t.Type; b = t; }
    virtual ~ColumnDef(void)
    {
    }
    void func(ColumnDefType nType)
    {
        switch(nType)
        {
            case T_byte:
            {
                byte_t *p = b.Adress;
                if(!p)
                    break;
            }
            break;
            case T_size_t:
            {
                size_t *p = st.Adress;
                if(!p)
                    break;
            }
            break;
            case T_ubyte:
            {
                ubyte_t *p = ub.Adress;
                if(!p)
                    break;
            }
            break;
            default:
                std::cout << "Unknown" << std::endl;
            break;
            }
    }
private:
     ColumnDefType mType;
     union
     {
        ubyte_type ub;
        date_type d;
        cpp_string_type cps;
        cstring_type cs;
        numeric<size_t, T_size_t> st;
        numeric<byte_t, T_byte> b;
     };
};
int main()
{
    std::string s = "value"; 
    date_t dt;
    char tst[5];
    size_t n;
    byte_t b;
    // Correct examples
    ColumnDef c0(ColumnDef::date_type::init(&dt, 0, ColumnDef::DF_YYYY_MM_TT));
    ColumnDef c1(ColumnDef::cstring_type::init(tst, sizeof(tst)));
    ColumnDef c2(ColumnDef::cpp_string_type::init(&s));
    ColumnDef c3(ColumnDef::numeric<byte_t, ColumnDef::T_byte>::init(&b, 0));
    ColumnDef c4(ColumnDef::numeric<size_t, ColumnDef::T_size_t>::init(&n, 0));
    // Wrong intialization causes a compiler error because type doesn't match enum. Only T_size_t should be allowed here.
    ColumnDef c5(ColumnDef::numeric<size_t, ColumnDef::T_std_string>::init(&n, 0));
    ColumnDef c6(ColumnDef::numeric<size_t, ColumnDef::T_byte>::init(&n, 0));
    ColumnDef c7(ColumnDef::numeric<size_t>::init(&n, 0));          // should assign the correct type automatically inferred by the supported type
    return 0;
}

您只需在类型和枚举值之间编写映射,然后在代码中重用它:

namespace details {
    template <typename T> struct TypeToEnum;
    template <>
    struct TypeToEnum<size_t> {
        static const ColumnDef::ColumnDefType value = ColumnDef::T_size_t;
    };
    template <>
    struct TypeToEnum<char> {
        static const ColumnDef::ColumnDefType value = ColumnDef::T_byte;
    };
}
template <typename T>
ColumnDef::numeric<T> ColumnDef::numeric<T>::init(T *p, T nDef) 
{
    ColumnDef::numeric<T> t = {details::TypeToEnum<T>::value, p, nDef};
    return t; 
}