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

时间:2015-11-06 11:27:11

标签: c++ templates

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

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

我正在使用支持C + 11子集的Visual Studio 2010。

#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;
}

1 个答案:

答案 0 :(得分: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; 
}