Blog Post

libcouchbase with C++ and threads (1/2)

Mark Nunberg of Couchbase Published

I decided to play around a bit last week trying to create a more standard set of C++ bindings for libcouchbase.

While libcouchbase is C and is thus fully usable from C++, I had a common and frequent itch to scratch when using libcouchbase in C++ programs -- namely there is no class hierarchy for command or response objects and the libcouchbase interface hardly feels "friendly". So I set out on a pet project to make a standard and generic (in the colloquial sense of the term) interface for libcouchbase in C++. This is a work in progress and may be found at https://github.com/couchbaselabs/lcb-cxx.

Because maximum compatibility for C++ was desired, I stayed away from using large external libraries like boost or C++11. These extra bindings can always be layered on top of the "generic" C++ bindings, while reversing the process may be more difficult.

The result was a set of bindings that exposed the following semantics:

Command Objects

All commands inherit from a Command object; e.g:

class Command { };

All key commands (i.e. set, get, delete) inherit from a KeyCommand object which derives from Command. The KeyCommand object has accessors for key and hashkey, e.g.

 
class KeyCommand : public Command {
    virtual void setKey(const std::string&) = 0;
    virtual void setHashKey(const std::string&) = 0;
}

Three template classes were created to aid with the construction of the command objects. They are instantiated with their T being the C libcouchbase structure which they wrap as their only data member - - and thus ensuring that the performance and memory profile of the C++ command class is more or less the same as the C struct (though there is a vtable). These templates provide common setters for commands which accept CAS and/or an expiration time, e.g.

 
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;
};
Response Objects

Like command objects, the response objects too are provided in a hierarchy; they contain the C lcb_resp_t * as their only data member. Their hierarchy is as follows:

  1. The abstract ResponseBase class. This features accessors for keys
  2. The abstract CasResponseBase class which inherits ResponseBase and provides accessors for the CAS
  3. The Response<T> class which implements ResponseBase retrieving the key information from T::v.v0.key
  4. The CasResponse<T> which inherits from Response<T> and implements CasResponseBase by providing cas via T::v.v0.cas
  5. Response-specific classes which provide extra information; e.g. GetResponse is implemented as CasResponse<lcb_get_cmd_t> with additional accessors for the value and flags.

In the header, this looks something like this:

 
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; }
};
 
 

Since the response classes are both derived from the templates as well as their pure abstract bases, they can have their common members treated by helper methods and classes, so for example, for a given function called logKey which logs the key from the response, it may be implemented like so:

 
void logKey(ResponseBase *resp) {
   std::cout << resp->getKey() << std::endl;
}

And then logKey may be called with any response object.

Callbacks

Finally I've also implemented a callback interface. Since libcouchbase is a C library and uses C callbacks, the following boilerplate for C++ code was rather common

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"

then, to set the callback

lcb_set_arithmetic_callback(instance, arith_handler);

In the new bindings, callbacks are exposed in a unified object; so that you don't have to explicitly set handlers - but simply subclass the ResponseHandler class and implement the onArithmetic(OperationContext *, const ArithmeticResponse *, lcb_error_t) method.

If you don't wish to implement dedicated handlers for generic commands, you can simply implement the onDefault(OperationContext *, const ResponseBase *, lcb_error_t) and handle all commands from there.