분류

C++ 및 스레드가 포함된 libcouchbase (1/2)

저는 지난 주에 libcouchbase를 위한 보다 표준적인 C++ 바인딩 세트를 만들어보기로 했습니다.

libcouchbase는 C언어이므로 C++에서 완전히 사용할 수 있지만, 저는 C++ 프로그램에서 libcouchbase를 사용할 때 명령 또는 응답 객체에 대한 클래스 계층 구조가 없고 libcouchbase 인터페이스가 "친숙하게" 느껴지지 않는다는 공통적이고 빈번한 가려움증을 느꼈습니다. 그래서 저는 표준적이고 일반적인(구어체적인 의미에서) C++ libcouchbase 인터페이스를 만들기 위한 펫 프로젝트에 착수했습니다. 이 프로젝트는 현재 진행 중이며 다음에서 확인할 수 있습니다. https://github.com/couchbaselabs/lcb-cxx.

C++에 대한 최대한의 호환성이 필요했기 때문에 다음과 같은 대규모 외부 라이브러리를 사용하지 않았습니다. boost 또는 C++11. 이러한 추가 바인딩은 항상 "일반" C++ 바인딩 위에 레이어링할 수 있지만, 그 과정을 되돌리기는 더 어려울 수 있습니다.

그 결과 다음과 같은 시맨틱을 노출하는 일련의 바인딩이 탄생했습니다:

명령 개체

모든 명령은 Command 개체에서 상속됩니다(예

Command 클래스 { };

모든 주요 명령(예: 설정, 가져오기, 삭제)은 다음과 같이 키 커맨드 객체에서 파생되는 명령. . 키 커맨드 객체에는 키와 해시 키에 대한 접근자가 있습니다.

 
클래스 키 커맨드 : public 명령 {
    가상 void setKey(const std::문자열&) = 0;
    가상 void setHashKey(const std::문자열&) = 0;
}

명령 객체의 구성을 돕기 위해 세 가지 템플릿 클래스가 만들어졌습니다. 이 템플릿은 유일한 데이터 멤버로 래핑하는 C libcouchbase 구조체를 T로 인스턴스화하여 C++ 명령 클래스의 성능 및 메모리 프로파일이 C 구조체와 거의 동일하도록 보장합니다(vtable이 있긴 하지만). 이러한 템플릿은 CAS 및/또는 만료 시간을 허용하는 명령에 대한 공통 설정자를 제공합니다(예

 
템플릿 <유형명 T> KeyCommand_v0 {
public:
       void setKey(const std::문자열 &s) {
           cmd.v.v0. = s.c_str();
           cmd.v.v0.nkey = s.크기();
      }
      // …
비공개:
    T cmd;
};
응답 개체

명령 객체와 마찬가지로 응답 객체도 계층 구조로 제공되며, 유일한 데이터 멤버로 C lcb_resp_t *를 포함합니다. 계층 구조는 다음과 같습니다:

  1. 초록 응답베이스 클래스. 여기에는 키에 대한 접근자가 있습니다.
  2. 초록 CasResponseBase 클래스를 상속하는 응답베이스 CAS에 대한 액세서리를 제공합니다.
  3. 그리고 응답 T::v.v0.key에서 키 정보를 검색하는 ResponseBase를 구현하는 클래스입니다.
  4. 그리고 CasResponse 에서 상속되는 응답 를 통해 cas를 제공하여 CasResponseBase를 구현합니다. T::v.v0.cas
  5. 추가 정보를 제공하는 응답별 클래스(예. GetResponse 는 다음과 같이 구현됩니다. CasResponse 값과 플래그에 대한 추가 접근자가 있습니다.

헤더에서는 다음과 같이 표시됩니다:

 
클래스 응답베이스 {
public:
    가상 const void *getKey(lcb_size_t *n) const = 0;
    std::문자열 getKey() const;
};
템플릿 <유형명 T, 클래스 I>
클래스 응답 : public I {
public:
    typedef T LcbInternalResponse;
    가상 const void * getKey(lcb_size_t *n) const {
        *n = resp>v.v0.nkey;
        반환 resp>v.v0.;
    }
보호됨:
    const T * resp;
};
템플릿 <유형명 T>
클래스 CasResponse : public 응답<T, CasResponseBase>
{
public:
    가상 lcb_cas_t getCas() const {
        반환 응답<T,CasResponseBase>::getRawResponse()>v.v0.cas;
    }
};
 
클래스 산술 응답 : public CasResponse<C_ArithResp> {
public:
    lcb_uint64_t getValue() const { 반환 getRawResponse()>v.v0.; }
};
 
 

응답 클래스는 템플릿과 순수한 추상 베이스에서 파생되므로 공통 멤버를 헬퍼 메서드와 클래스로 처리할 수 있으므로, 예를 들어 응답에서 키를 기록하는 logKey라는 함수의 경우 다음과 같이 구현할 수 있습니다:

 
void 로그키(응답베이스 *resp) {
   std::cout << resp>getKey() << std::endl;
}

그런 다음 모든 응답 객체와 함께 로그키를 호출할 수 있습니다.

콜백

마지막으로 콜백 인터페이스도 구현했습니다. libcouchbase는 C 라이브러리이고 C 콜백을 사용하기 때문에 C++ 코드에 대한 다음과 같은 상용구는 다소 일반적이었습니다.

외부 "C" {
정적 void arith_handler(lcb_t, const void *쿠키, lcb_error_t 오류, const LCB_산술_응답_t *resp) {
    MyCppObject *o = 재 해석_캐스트<MyCppObject>(const_cast<void*>(쿠키));
    o>doSomething(resp);
}
} // 외부 "C"

를 클릭한 다음 콜백을 설정하려면

LCB_SET_ARITHMIC_CALLBACK(인스턴스, arith_handler);

새로운 바인딩에서는 콜백이 통합된 객체에 노출되므로 핸들러를 명시적으로 설정할 필요 없이 ResponseHandler 클래스를 서브클래싱하고 onArithmetic(OperationContext *, const ArithmeticResponse *, lcb_error_t) 메서드를 사용합니다.

일반 명령에 대한 전용 핸들러를 구현하고 싶지 않다면 간단히 onDefault(OperationContext *, const ResponseBase *, lcb_error_t) 를 클릭하고 거기에서 모든 명령을 처리합니다.

이 문서 공유하기
받은 편지함에서 카우치베이스 블로그 업데이트 받기
이 필드는 필수 입력 사항입니다.

작성자

게시자 마크 넌버그, 소프트웨어 엔지니어, 카우치베이스

마크 넌버그는 카우치베이스에서 일하는 소프트웨어 엔지니어입니다. 그는 C 클라이언트 라이브러리(libcouchbase)와 Python 클라이언트를 유지 관리합니다. 또한 이전 회사에서 사용하던 Perl 클라이언트도 개발했는데, 이것이 Couchbase에서 일하게 된 계기가 되었습니다. Couchbase에 입사하기 전에는 전자상거래 분석 회사에서 분산형 고성능 라우팅 시스템을 개발했습니다. Mark는 예루살렘 히브리 대학교에서 언어학을 전공했습니다.

댓글 하나

  1. [...] 금주의 블로그 포스트 #2 : C++와 스레드가 있는 libcouchbase (1/2)금주의 질문 : 스왑 리밸런싱의 시간 비용에 대한 질문 [...]

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

구축 시작

개발자 포털에서 NoSQL을 살펴보고, 리소스를 찾아보고, 튜토리얼을 시작하세요.

카펠라 무료 사용

클릭 몇 번으로 Couchbase를 직접 체험해 보세요. Capella DBaaS는 가장 쉽고 빠르게 시작할 수 있는 방법입니다.

연락하기

카우치베이스 제품에 대해 자세히 알고 싶으신가요? 저희가 도와드리겠습니다.