【发布时间】:2015-11-06 11:27:11
【问题描述】:
我正在尝试将数据类型从文件映射到内存,所以我有一个类来获取每一列的信息。 不同的类型有不同的映射参数,所以我为每种支持的类型创建了一个带有适当参数的结构。我设法以这种方式解决了类型安全的问题,现在我正在使用一个模板,即所有共享相同属性的数字类型,只有类型不同。 唯一仍然相当难看的是,我不能仅仅根据支持的类型来分配正确的枚举。
如下例所示,如果是数字类型,我必须手动分配关联类型(示例代码中的c5 和c6)。现在我想知道是否有更优雅的解决方案,以便我可以使用一些模板技术(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<size_t, ColumnDef::T_std_string>::init(&n, 0),ColumnDef::numeric<size_t, ColumnDef::T_byte>::init(&n, 0),基于应该从enum中选择什么具体值? -
是的,这只是为了演示错误的初始化,这将导致编译器错误。但是,我想要实现的是,如果像 c7 一样给出正确的类型,则可以通过模板推断出正确的类型。我目前正在尝试使用 enable_if 但我不太了解。
-
所以当我使用枚举 T_size_t 时,size_t 是唯一允许的类型(或者如果这样更容易,则相反)。 IE。当我给一个 size_t 类型时,它应该选择 T_size_t 作为枚举类型。
-
这让我尖叫,你需要模板化
ColumnDef。但这也需要大规模的程序重构,因为这意味着传递给ColemnDef的任何东西也必须模板化。你真正在这里制作的是一个容器。描述包含类型的枚举是一种 C 策略。请注意所有标准容器只需typedef它们包含的类型。如果您有兴趣解决其余的架构更改,我可以为您输入一个模板化的ColemnDef。 -
您是否想要(功能上、性能上、符号上?)
boost::variant还没有做得很好,或者这是一个学习练习?