我有一个OracleConnection类,它使用OCCI Oracle API来访问数据库。我现在需要从数据库中获取多行记录,这是通过API的ResultSet :: getDataBuffer(...)函数完成的。这个函数接受一系列参数,其中一个是一个很大的枚举,定义了可以包含的数据类型。
显然我不想用Oracle API类型来扩展我的应用程序代码,因此其他API可以与这个API互换。所以我的问题是如何在函数包装器中最好地使用这个Type参数?我应该只创建一个枚举,只采用我需要的类型,或者模板可以帮我在这里映射到我在OracleConnection类中的OCCI枚举吗?
Occi setDataBuffer功能:
void setDataBuffer(
unsigned int colIndex,
void *buffer,
Type type,
sb4 size = 0,
ub2 *length = NULL,
sb2 *ind = NULL,
ub2 *rc = NULL);
Type
这是一个看起来像这样的枚举:
enum Type
{
OCCI_SQLT_CHR=SQLT_CHR,
OCCI_SQLT_NUM=SQLT_NUM,
OCCIINT = SQLT_INT,
OCCIFLOAT = SQLT_FLT,
OCCIBFLOAT = SQLT_BFLOAT,
OCCIBDOUBLE = SQLT_BDOUBLE,
OCCIIBFLOAT = SQLT_IBFLOAT,
OCCIIBDOUBLE = SQLT_IBDOUBLE,
OCCI_SQLT_STR=SQLT_STR,
OCCI_SQLT_VNU=SQLT_VNU,
OCCI_SQLT_PDN=SQLT_PDN,
OCCI_SQLT_LNG=SQLT_LNG,
OCCI_SQLT_VCS=SQLT_VCS,
.... (about 2x as many to go)
我的包装器如下所示:
void setDataBuffer(unsigned int colIndex, void * buffer, unsigned long size = 0, int type /*use int or template or redefine own Type Enum?*/, unsigned short * length = NULL, signed short * ind = NULL, unsigned short * rc = NULL)
答案 0 :(得分:3)
一个选项可能是使您的函数成为模板,然后使用traits类将模板类型转换为表示各种Oracle类型的值。
特质类可能如下所示:
template <typename T>
struct oracle_type_traits;
template <> // create a specialization for each relevant type
struct oracle_type_traits<double> {
static const value = OCCIBDOUBLE // its value member should be the value you want to map to
};
现在,以下内容将为您提供double
:
oracle_type_traits<double>::value
并在setDataBuffer<T>(...)
内,您只需检查oracle_type_traits<T>::value
即可获取相应的Oracle类型ID。
答案 1 :(得分:2)
从你的包装器的用户的POV,最好的是他们会调用一个重载的函数或一个函数(成员)模板,他们将一个对象传递给适当的类型,然后神奇地做正确的那种类型的东西。也就是说,最好的是为您的类(或Oracle API)支持的任何类型getData(unsigned int colIndex, T&)
提供函数T
,这将找出必要的缓冲区大小,分配缓冲区,确定正确的枚举,并调用Oracle API函数
我相信你可以解决大部分细节,可能除了如何将类型映射到enum
之外,所以这就是我要尝试排除的内容。
基本上,我看到两种可能性,其中一种(使用编译时列表)更适合如果你有很多类型需要支持,而另一种(使用特征)需要使用,如果还有更多特定于类型的,而不仅仅是将类型映射到enum
。
traits方法使用起来非常简单,但如果你有很多类型,那就太乏味了:
template<typename T>
struct get_data_buffer_traits;
template<>
struct get_data_buffer_traits<int> {
Type type OCCIINT;
};
template<>
struct get_data_buffer_traits<float> {
Type type OCCIBFLOAT;
};
然后,您可以使用T
将传递给模板的类型enum
映射到右get_data_buffer_traits<T>::type
值。
此traits模板也是您可以放置通用数据检索功能可能需要的任何其他类型特定操作的地方(例如,在缓冲区中的内容与实际类型之间进行转换,如果这不是直接转换)。如果您没有其他任何东西可以放入这些特征,您可以使用宏来更容易地定义这些:
#define DEFINE_GET_DATA_BUFFER_TRAITS(Type_,Enum_) \
template<> struct get_data_buffer_traits<Type_> { Type type Enum_; };
DEFINE_GET_DATA_BUFFER_TRAITS(int , OCCIINT );
DEFINE_GET_DATA_BUFFER_TRAITS(float, OCCIBFLOAT);
...
#undef DEFINE_GET_DATA_BUFFER_TRAITS
但是,如果是这种情况,您也可以创建一个编译时映射,映射两者并在(编译时)搜索右enum
值。如果你手头没有提供这个的模板元库,这里有一个想法如何自己做的概述:
// Beware, brain-compiled code ahead!
struct nil {};
template< typename HType
, Type HEnum
, class T >
struct set_data_buffer_type_map_node {
typedef HType head_type
enum { head_enum = HEnum };
typedef T tail_type;
};
typedef
set_data_buffer_type_map_node< int , OCCIINT
set_data_buffer_type_map_node< float, OCCIBFLOAT
...
nil
> > // either count or keep adding these until compiler accepts :)
set_data_buffer_type_map;
template< typename T, class Map >
struct getter {
// recurse towards tail
Type get_enum() { return getter<T,typename Map::tail_type>::get_enum(); }
};
template< typename T, Type HEnum, class Tail >
struct getter< T, set_data_buffer_type_map<T,HEnum,Tail> > {
// current node has T as HType
Type get_enum() { return set_data_buffer_type_map<T,HEnum,Tail>::head_enum; }
};
template< typename T, typename HType, Type HEnum, >
struct getter< T, set_data_buffer_type_map<T,HEnum,nil> > {
// no function here, so compile-time error
};
template< typename T>
Type get_type_enum()
{
return getter<T, set_data_buffer_type_map>::get_enum();
}
(注意:这只是一个大纲。我甚至没有尝试过编译它。)
答案 2 :(得分:0)
我建议使用枚举选项。使用它作为模板意味着您的API用户应该知道所有类型之前可能有点困难。将它作为枚举使用也可以选择引用枚举并确定哪种SQL类型符合要求。