Первоначально созданный Марком Нунбергом,
я решил немного поиграть на прошлой неделе, пытаясь создать более стандартный набор привязок C ++ для libcouchbase.
Хотя libcouchbase является C и, таким образом, полностью используется из C ++, у меня был общий и частый зуд при использовании libcouchbase в программах на C ++ — а именно, отсутствует иерархия классов для объектов команд или ответов, и интерфейс libcouchbase едва ли чувствует себя «дружественным». Поэтому я решил создать проект для стандартного и универсального (в разговорном смысле этого слова) интерфейса для libcouchbase в C ++. Эта работа находится в стадии разработки и может быть найдена по адресу https://github.com/couchbaselabs/lcb-cxx .
Поскольку требовалась максимальная совместимость для C ++, я избегал использования больших внешних библиотек, таких как boost или C ++ 11 . Эти дополнительные привязки всегда можно наложить поверх «общих» привязок C ++, в то время как процесс может быть более сложным.
Результатом стал набор связываний, которые раскрыли следующую семантику:
Командные объекты
Все команды наследуются от объекта Command; например:
команда класса {};
Все ключевые команды (т.е. set, get, delete) наследуются от объекта KeyCommand, который наследуется от Command . Объект KeyCommand имеет средства доступа для ключа и хэш-ключа, например
class KeyCommand : public Command { virtual void setKey(const std::string&) = 0; virtual void setHashKey(const std::string&) = 0; }
Три шаблонных класса были созданы для помощи в создании объектов команд. Они создаются с их T, являющимся структурой libcouchbase C, которую они обертывают как единственный элемент данных — и, таким образом, гарантируют, что производительность и профиль памяти класса команд C ++ более или менее совпадают со структурой C (хотя существует виртуальные таблицы). Эти шаблоны предоставляют общие установщики для команд, которые принимают CAS и / или время истечения, например
template <typename T> KeyCommand_v0 { public: void setKey(const std::string &s) { cmd.v.v0.key = s.c_str(); cmd.v.v0.nkey = s.size(); } // ... private: T cmd; };
Объекты ответа
Как и объекты команд, объекты ответа также представлены в иерархии; они содержат C lcb_resp_t * в качестве единственного элемента данных. Их иерархия выглядит следующим образом:
- Абстрактный класс ResponseBase . Это функции аксессуаров для ключей
- Абстрактный класс CasResponseBase, который наследует ResponseBase и предоставляет средства доступа для CAS
- Класс Response <T>, который реализует ResponseBase, получая ключевую информацию из T :: v.v0.key
- CasResponse <T> , который наследует от Response <T> и реализует CasResponseBase, предоставляя КАС с помощью T :: v.v0.cas
- Классы для ответов, которые предоставляют дополнительную информацию; Например, GetResponse реализован как CasResponse <lcb_get_cmd_t> с дополнительными средствами доступа для значения и флагов.
В шапке это выглядит примерно так:
class ResponseBase { public: virtual const void *getKey(lcb_size_t *n) const = 0; std::string getKey() const; };
template <typename T, class I> class Response : public I { public: typedef T LcbInternalResponse; virtual const void * getKey(lcb_size_t *n) const { *n = resp->v.v0.nkey; return resp->v.v0.key; } protected: const T * resp; };
template <typename T> class CasResponse : public Response<T, CasResponseBase> { public: virtual lcb_cas_t getCas() const { return Response<T,CasResponseBase>::getRawResponse()->v.v0.cas; } };
class ArithmeticResponse : public CasResponse<C_ArithResp> { public: lcb_uint64_t getValue() const { return getRawResponse()->v.v0.value; } };
Так как классы ответов являются производными как от шаблонов, так и от их чистых абстрактных баз, их общие члены могут обрабатываться вспомогательными методами и классами, например, для заданной функции с именем logKey, которая записывает ключ из ответа, он может быть реализовано так:
void logKey(ResponseBase *resp) { std::cout << resp->getKey() << std::endl; }
И тогда logKey может быть вызван с любым объектом ответа.
Callbacks
Наконец, я также реализовал интерфейс обратного вызова. Поскольку libcouchbase является библиотекой C и использует обратные вызовы C, следующий шаблон для кода C ++ был довольно распространенным:
extern "C" { static void arith_handler(lcb_t, const void *cookie, lcb_error_t err, const lcb_arithmetic_response_t *resp) { MyCppObject *o = reinterpret_cast<MyCppObject>(const_cast<void*>(cookie)); o->doSomething(resp); } } // extern "C"
затем, чтобы установить обратный вызов
lcb_set_arithmetic_callback(instance, arith_handler);
В новых привязках обратные вызовы отображаются в едином объекте; так что вам не нужно явно устанавливать обработчики, а просто создать подкласс класса ResponseHandler и реализовать метод onArithmetic (OperationContext *, const ArithmeticResponse *, lcb_error_t) .
Если вы не хотите реализовывать выделенные обработчики для общих команд, вы можете просто реализовать onDefault (OperationContext *, const ResponseBase *, lcb_error_t) и обрабатывать все команды оттуда.