DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now
  1. DZone
  2. Coding
  3. Languages
  4. Libcouchbase with C++ and Threads (1/2)
Content provided by Couchbase logo

Libcouchbase with C++ and Threads (1/2)

Don Pinto user avatar by
Don Pinto
·
Oct. 11, 13 · Interview
Like (0)
Save
Tweet
Share
4.15K Views

Originally authored by Mark Nunberg

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.



Comments

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: