Практика, касающаяся обертки для setDataBuffer (OCCI) - PullRequest
1 голос
/ 05 апреля 2011

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

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

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 ]

3 голосов
/ 05 апреля 2011

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

2 голосов
/ 05 апреля 2011

Из POV пользователей вашей оболочки лучше всего было бы, если бы они вызывали либо перегруженную функцию, либо шаблон функции (члена), которому они передали объект соответствующего типа и который затем волшебным образом сделал бы правильноевещь для этого типа.То есть лучше всего иметь функцию 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();
}

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

0 голосов
/ 05 апреля 2011

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

...