Первоначально созданный Марком Нунбергом,
я решил немного поиграть на прошлой неделе, пытаясь создать более стандартный набор привязок 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) и обрабатывать все команды оттуда.