Практика использования оболочки для setDataBuffer (OCCI)

У меня есть класс OracleConnection, который использует API OCCI Oracle для доступа к базе данных. Теперь мне нужно получить несколько строк записей из базы данных, и это делается с помощью функции ResultSet::getDataBuffer(...) API. Эта функция принимает ряд аргументов, один из которых представляет собой большое перечисление, определяющее типы данных, которые могут содержаться.

Очевидно, я не хочу загромождать код своего приложения типами Oracle API, поэтому другие API можно заменить этим. Итак, мой вопрос: как мне лучше всего использовать этот параметр Type в моей функциональной оболочке? Должен ли я просто создать перечисление и взять только те типы, которые мне понадобятся, или шаблоны могут помочь мне здесь сопоставить перечисление OCCI в имеющемся у меня классе OracleConnection?

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)

person Tony The Lion    schedule 05.04.2011    source источник


Ответы (3)


Один из вариантов может состоять в том, чтобы сделать вашу функцию шаблоном, а затем использовать класс свойств для преобразования типа шаблона в значения, представляющие различные типы 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
};

Теперь следующее даст вам идентификатор типа Oracle для double:

oracle_type_traits<double>::value

и внутри setDataBuffer<T>(...) вы просто проверяете oracle_type_traits<T>::value, чтобы получить соответствующий идентификатор типа Oracle.

person jalf    schedule 05.04.2011
comment
Через час после того, как Тони задал вопрос, вы публикуете этот ответ за 90 секунд до того, как я опубликую, по сути, то же самое! :) Во всяком случае, +1 от меня. - person sbi; 05.04.2011

С точки зрения пользователей вашей оболочки, лучше всего было бы, если бы они вызывали либо перегруженную функцию, либо шаблон функции (члена), которому они передают объект соответствующего типа, и который затем волшебным образом делает то, что нужно для этого. тип. То есть лучше всего было бы иметь функцию getData(unsigned int colIndex, T&) для любого типа T, поддерживаемого вашим классом (или Oracle API), который узнает необходимый размер буфера, выделит буфер, определит правильное перечисление и вызовет функцию Oracle API .
Я уверен, что вы сможете проработать большинство деталей, возможно, за исключением того, как сопоставить тип с enum, поэтому я попытаюсь обрисовать именно это.

По сути, я вижу две возможности для этого, одна из которых (использование списка времени компиляции) лучше подходит, если у вас есть много поддерживаемых типов, а другая (использование трейтов) необходимо использовать, если есть более специфичные для типа к этому, чем просто сопоставление типа с enum.

Метод признаков довольно прост в использовании, но утомителен, если у вас много типов:

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.

Этот шаблон признаков также является местом, где вы можете поместить любую другую операцию, специфичную для типа, которая может понадобиться вашей универсальной функции извлечения данных (например, преобразование между тем, что находится в буфере, и фактическим типом, если это не прямое приведение). Если вам больше нечего добавить в эти трейты, вы можете использовать макрос, чтобы упростить их определение:

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

(Примечание: это всего лишь набросок. Я даже не пытался его компилировать.)

person sbi    schedule 05.04.2011
comment
это выглядит действительно интересно и полезно, однако что вы подразумеваете под этим, если у вас нет под рукой метабиблиотеки шаблонов, которая обеспечивает это? - person Tony The Lion; 05.04.2011
comment
@Tony: Я полагаю, что мета-библиотека программирования boost предоставит некоторый тип отображения во время компиляции (я точно не знаю, так как никогда его не использовал), и другие мета-библиотеки шаблонов также могут предоставить это. - person sbi; 06.04.2011

Я предлагаю пойти с опцией перечисления. Использование его в качестве шаблона означает, что пользователи вашего API должны иметь представление обо всех типах, с которыми может быть немного сложно. Использование его в качестве перечисления также дает им возможность ссылаться на перечисление и решать, какие типы SQL соответствуют требованиям.

person user258367    schedule 05.04.2011