关于setDataBuffer(OCCI)的包装器的实践

时间:2011-04-05 11:50:20

标签: c++ oracle wrapper

我有一个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)

3 个答案:

答案 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类型ID
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类型符合要求。