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

Practices regarding wrapper for setDataBuffer (OCCI)

本文关键字:包装 setDataBuffer OCCI 关于      更新时间:2023-10-16

我有一个OracleConnection类,使用OCCI Oracle API访问数据库。我现在需要从数据库中获取多行记录,这是用API的ResultSet::getDataBuffer(…)函数完成的。这个函数接受一系列参数,其中一个是一个大enum,它定义了数据可以包含的类型。

显然,我不想让我的应用程序代码与Oracle API类型,所以其他API可以与这个交换。所以我的问题是我如何最好地采取这个类型参数在我的函数包装?我应该只是创建一个枚举,只采取我需要的类型,或者模板可以帮助我在这里映射到OCCI的枚举在oraclecconnection类我有?

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:

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)

一种选择是让你的函数成为模板,然后使用trait类将模板类型转换为代表各种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。

从包装器用户的POV来看,最好的情况是他们调用重载函数或函数(成员)模板,并向其传递适当类型的对象,然后该对象将神奇地为该类型做正确的事情。也就是说,对于类(或Oracle API)支持的任何类型T,最好有一个getData(unsigned int colIndex, T&)函数,它将找出必要的缓冲区大小,分配缓冲区,确定正确的enum,并调用Oracle API函数。我相信你可以找出大部分的细节,可能除了如何映射一个类型到enum,所以这是我将尝试出线。

基本上,我认为这有两种可能性,如果你有很多类型要支持,其中一种(使用编译时列表)更适合,而另一种(使用trait)需要使用,如果有更多类型特定于此,而不仅仅是将类型映射到enum

trait方法使用起来很简单,但是如果你有很多类型的话就会很繁琐了:

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

然后,您可以使用get_data_buffer_traits<T>::type将传递给模板的类型作为T映射到正确的enum值。

这个trait模板也是可以放置泛型数据检索函数可能需要的任何其他特定于类型的操作的地方(比如在缓冲区中的内容和实际类型之间进行转换,如果不是直接强制转换的话)。如果你没有其他东西可以放入这些特征中,你可以使用一个宏来更容易地定义它们:

#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();
}

(注:这只是一个提纲。我甚至没有尝试编译它。)

我建议使用enum选项。使用它作为模板意味着您的API用户应该了解所有类型之前,这可能有点困难。使用它作为枚举也给他们一个选项来引用枚举,并决定哪些SQL类型适合需求。