【问题标题】:Ensure that enum corresponds to a certain template type确保枚举对应于某个模板类型
【发布时间】:2015-11-06 11:27:11
【问题描述】:

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

如下例所示,如果是数字类型,我必须手动分配关联类型(示例代码中的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;
}

【问题讨论】:

  • 你做同样的事情,但使用不同的枚举:ColumnDef::numeric&lt;size_t, ColumnDef::T_std_string&gt;::init(&amp;n, 0)ColumnDef::numeric&lt;size_t, ColumnDef::T_byte&gt;::init(&amp;n, 0),基于应该从enum中选择什么具体值?
  • 是的,这只是为了演示错误的初始化,这将导致编译器错误。但是,我想要实现的是,如果像 c7 一样给出正确的类型,则可以通过模板推断出正确的类型。我目前正在尝试使用 enable_if 但我不太了解。
  • 所以当我使用枚举 T_size_t 时,size_t 是唯一允许的类型(或者如果这样更容易,则相反)。 IE。当我给一个 size_t 类型时,它应该选择 T_size_t 作为枚举类型。
  • 这让我尖叫,你需要模板化 ColumnDef。但这也需要大规模的程序重构,因为这意味着传递给ColemnDef 的任何东西也必须模板化。你真正在这里制作的是一个容器。描述包含类型的枚举是一种 C 策略。请注意所有标准容器只需 typedef 它们包含的类型。如果您有兴趣解决其余的架构更改,我可以为您输入一个模板化的ColemnDef
  • 您是否想要(功能上、性能上、符号上?)boost::variant 还没有做得很好,或者这是一个学习练习?

标签: c++ templates


【解决方案1】:

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

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

【讨论】:

  • 我知道我必须使用一些帮助模板,但我不明白如何实现它,因为我对更高级的模板技术还是比较陌生。 :) 你能解释一下为什么需要template&lt;&gt; 吗?
  • template&lt;&gt; 是针对具体类型的模板特化,所以TypeToEnum 只为size_tchar 定义,所有其他变体都会导致编译时错误。
猜你喜欢
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多