C++ 关于setDataBuffer(OCCI)包装器的实践
我有一个OracleConnection类,它使用OCCI Oracle API访问数据库。我现在需要从数据库中获取多行记录,这是通过API的ResultSet::getDataBuffer(…)函数完成的。此函数接受一系列参数,其中一个参数是定义可以包含的数据类型的大型枚举 显然,我不想用oracleapi类型来扩展我的应用程序代码,所以其他API可以与这个API互换。所以我的问题是如何在函数包装器中最好地使用这个类型参数?我应该创建一个枚举并只使用我需要的类型,还是模板可以帮助我在这里映射到OracleConnection类中OCCI的枚举 Occi功能:C++ 关于setDataBuffer(OCCI)包装器的实践,c++,oracle,wrapper,C++,Oracle,Wrapper,我有一个OracleConnection类,它使用OCCI Oracle API访问数据库。我现在需要从数据库中获取多行记录,这是通过API的ResultSet::getDataBuffer(…)函数完成的。此函数接受一系列参数,其中一个参数是定义可以包含的数据类型的大型枚举 显然,我不想用oracleapi类型来扩展我的应用程序代码,所以其他API可以与这个API互换。所以我的问题是如何在函数包装器中最好地使用这个类型参数?我应该创建一个枚举并只使用我需要的类型,还是模板可以帮助我在这里映射到
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)
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
};
我的包装器如下所示:
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)
我建议使用enum选项。使用它作为模板意味着您的API用户应该了解之前可能有点困难的所有类型。使用它作为枚举还可以让它们作为引用枚举的选项,并决定哪些SQL类型符合要求。我建议使用枚举选项。使用它作为模板意味着您的API用户应该了解之前可能有点困难的所有类型。使用它作为枚举还可以让它们作为引用枚举的选项,并决定哪些SQL类型符合要求。一个选项可以是将函数设置为模板,然后使用traits类将模板类型转换为表示各种Oracle类型的值 traits类可以如下所示:
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)
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
};
在
setDataBuffer(…)
中,只需选中oracle\u type\u traits::value
即可获得相应的oracle类型ID。一个选项是将函数设置为模板,然后使用traits类将模板类型转换为表示各种oracle类型的值
traits类可以如下所示:
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)
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
};
在
setDataBuffer(…)
内部,您只需检查oracle\u type\u traits::value
即可获得相应的oracle类型ID。从包装器用户的POV中,最好是他们调用重载函数或函数(成员)他们将对象传递给适当类型的模板,然后该模板将神奇地为该类型做正确的事情。也就是说,对于您的类(或Oracle API)支持的任何类型,最好都有一个函数getData(unsigned int colIndex,T&)
,该函数将找出必要的缓冲区大小,分配缓冲区,确定正确的枚举,并调用Oracle API函数。我相信您可以解决大部分细节,可能除了如何将类型映射到
枚举
,所以这就是我将尝试解决的问题
基本上,我认为这有两种可能,其中一种(使用编译时列表)更适合支持大量类型,而另一种(使用特征)则需要使用,前提是有更多特定于此的类型,而不仅仅是将一个类型映射到枚举
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;
};
但是,如果是这种情况,您还可以创建一个编译时映射,将两者映射在一起,并(在编译时)搜索右侧的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();
}
//当心,brain提前编译了代码!
结构nil{};
模板
结构集\数据\缓冲区\类型\映射\节点{
typedef HType head_type
枚举{head_enum=HEnum};
类型def T tail_类型;
};
类型定义
设置数据缓冲区类型映射节点>//计算或继续添加这些,直到编译器接受:)
设置数据缓冲区类型映射;
模板
结构吸气剂{
//向尾部递归
键入get_enum(){return getter::get_enum();}
};
模板
struct getter{
//当前节点具有作为HType的T
类型get_enum(){return set_data_buffer_Type_map::head_enum;}
};
模板
struct getter{
//这里没有函数,所以编译时出错
};
模板
类型get_类型_枚举()
{
返回getter::get_enum();
}
(注意:这只是一个大纲。我甚至没有试图编译它。)从包装器用户的角度来看,最好是他们调用重载函数或函数(成员)模板,将适当类型的对象传递给该模板,然后该模板将神奇地为该类型做正确的事情。也就是说,对于您的类(或Oracle API)支持的任何类型,最好都有一个函数
getData(unsigned int colIndex,T&)
,该函数将找出必要的缓冲区大小,分配缓冲区,确定正确的枚举,并调用Oracle API函数。我相信您可以解决大部分细节,可能除了如何将类型映射到
枚举
,所以这就是我将尝试解决的问题
基本上,我认为这有两种可能,其中一种(使用编译时列表)更适合支持大量类型,而另一种(使用特征)则需要使用,前提是有更多特定于此的类型,而不仅仅是将一个类型映射到枚举
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;
};
但是,如果是这种情况,您还可以创建一个编译时映射,将两者映射在一起,并(在编译时)搜索右侧的enum
值。如果你没有模板