Decidí jugar un poco la semana pasada tratando de crear un conjunto más estándar de enlaces C ++ para libcouchbase.
Aunque libcouchbase es C y por lo tanto es totalmente utilizable desde C++, yo tenía un prurito común y frecuente que rascar cuando usaba libcouchbase en programas C++ - a saber, no hay jerarquía de clases para objetos de comando o respuesta y la interfaz de libcouchbase difícilmente se siente "amigable". Así que me embarqué en un proyecto para hacer una interfaz estándar y genérica (en el sentido coloquial del término) para libcouchbase en C++. Este es un trabajo en progreso y se puede encontrar en https://github.com/couchbaselabs/lcb-cxx.
Dado que se buscaba la máxima compatibilidad con C++, evité utilizar grandes bibliotecas externas como impulsar o C++11. Estos bindings adicionales siempre pueden superponerse a los bindings C++ "genéricos", mientras que invertir el proceso puede resultar más difícil.
El resultado fue un conjunto de enlaces que exponían la siguiente semántica:
Objetos de mando
Todos los comandos heredan de un objeto Command; por ejemplo
clase Comando { };
Todos los comandos de teclas (es decir, set, get, delete) heredan de un comando KeyCommand que deriva de Comando. En KeyCommand dispone de accesores para key y hashkey, por ejemplo
virtual void setKey(const std::cadena&) = 0;
virtual void setHashKey(const std::cadena&) = 0;
}
Se han creado tres clases de plantillas para facilitar la construcción de los objetos comando. Se instancian con su T siendo la estructura C libcouchbase que envuelven como su único miembro de datos - - y asegurando así que el rendimiento y el perfil de memoria de la clase de comando C++ es más o menos el mismo que la estructura C (aunque hay una vtable). Estas plantillas proporcionan setters comunes para comandos que aceptan CAS y/o un tiempo de expiración, por ejemplo
público:
void setKey(const std::cadena &s) {
cmd.v.v0.clave = s.c_str();
cmd.v.v0.nkey = s.talla();
}
// …
privado:
T cmd;
};
Objetos de respuesta
Al igual que los objetos de comando, los objetos de respuesta también se proporcionan en una jerarquía; contienen el C lcb_resp_t * como su único miembro de datos. Su jerarquía es la siguiente:
- Resumen ResponseBase clase. Esta clase dispone de accesorios para las claves
- Resumen CasResponseBase que hereda de ResponseBase y proporciona accesores para el CAS
- En Respuesta
clase que implementa ResponseBase recuperando la información clave de T::v.v0.key - En CasResponse
e implementa CasResponseBase proporcionando cas a través de T::v.v0.cas - Clases específicas de respuesta que proporcionan información adicional; por ejemplo GetResponse se implementa como CasResponse
con accesores adicionales para el valor y las banderas.
En la cabecera, tiene este aspecto:
público:
virtual const void *getKey(lcb_size_t *n) const = 0;
std::cadena getKey() const;
};
clase Respuesta : público I {
público:
typedef T LcbInternalResponse;
virtual const void * getKey(lcb_size_t *n) const {
*n = resp–>v.v0.nkey;
devolver resp–>v.v0.clave;
}
protegido:
const T * resp;
};
clase CasResponse : público Respuesta<T, CasResponseBase>
{
público:
virtual lcb_cas_t getCas() const {
devolver Respuesta<T,CasResponseBase>::getRawResponse()–>v.v0.cas;
}
};
público:
lcb_uint64_t getValue() const { devolver getRawResponse()–>v.v0.valor; }
};
Dado que las clases response derivan tanto de las plantillas como de sus bases abstractas puras, pueden tener sus miembros comunes tratados por métodos y clases helper, así por ejemplo, para una función dada llamada logKey que registra la clave de la respuesta, puede implementarse así:
std::cout << resp–>getKey() << std::endl;
}
Y entonces logKey puede ser llamado con cualquier objeto de respuesta.
Devoluciones de llamada
Por último, también he implementado una interfaz de devolución de llamada. Como libcouchbase es una librería en C y usa callbacks en C, el siguiente boilerplate para código C++ era bastante común
estático void arith_handler(lcb_t, const void *cookie, lcb_error_t err, const lcb_arithmetic_response_t *resp) {
MyCppObject *o = reinterpretar_cast<MyCppObject>(const_cast<void*>(galleta));
o–>hacerAlgo(resp);
}
} // extern "C"
a continuación, para establecer la devolución de llamada
En las nuevas vinculaciones, las retrollamadas se exponen en un objeto unificado, de modo que no es necesario configurar explícitamente los manejadores, sino que basta con subclasificar la clase ResponseHandler e implementar la función onArithmetic(OperationContext *, const ArithmeticResponse *, lcb_error_t) método.
Si no desea implementar manejadores dedicados para comandos genéricos, puede simplemente implementar la función onDefault(OperationContext *, const ResponseBase *, lcb_error_t) y manejar todos los comandos desde allí.
[...] Blog Post of the Week #2 : libcouchbase with C++ and threads (1/2)Question of the Week : Pregunta sobre el coste temporal del reequilibrio de swaps [...]