저는 지난 주에 libcouchbase를 위한 보다 표준적인 C++ 바인딩 세트를 만들어보기로 했습니다.
libcouchbase는 C언어이므로 C++에서 완전히 사용할 수 있지만, 저는 C++ 프로그램에서 libcouchbase를 사용할 때 명령 또는 응답 객체에 대한 클래스 계층 구조가 없고 libcouchbase 인터페이스가 "친숙하게" 느껴지지 않는다는 공통적이고 빈번한 가려움증을 느꼈습니다. 그래서 저는 표준적이고 일반적인(구어체적인 의미에서) C++ libcouchbase 인터페이스를 만들기 위한 펫 프로젝트에 착수했습니다. 이 프로젝트는 현재 진행 중이며 다음에서 확인할 수 있습니다. https://github.com/couchbaselabs/lcb-cxx.
C++에 대한 최대한의 호환성이 필요했기 때문에 다음과 같은 대규모 외부 라이브러리를 사용하지 않았습니다. boost 또는 C++11. 이러한 추가 바인딩은 항상 "일반" C++ 바인딩 위에 레이어링할 수 있지만, 그 과정을 되돌리기는 더 어려울 수 있습니다.
그 결과 다음과 같은 시맨틱을 노출하는 일련의 바인딩이 탄생했습니다:
명령 개체
모든 명령은 Command 개체에서 상속됩니다(예
Command 클래스 { };
모든 주요 명령(예: 설정, 가져오기, 삭제)은 다음과 같이 키 커맨드 객체에서 파생되는 명령. . 키 커맨드 객체에는 키와 해시 키에 대한 접근자가 있습니다.
가상 void setKey(const std::문자열&) = 0;
가상 void setHashKey(const std::문자열&) = 0;
}
명령 객체의 구성을 돕기 위해 세 가지 템플릿 클래스가 만들어졌습니다. 이 템플릿은 유일한 데이터 멤버로 래핑하는 C libcouchbase 구조체를 T로 인스턴스화하여 C++ 명령 클래스의 성능 및 메모리 프로파일이 C 구조체와 거의 동일하도록 보장합니다(vtable이 있긴 하지만). 이러한 템플릿은 CAS 및/또는 만료 시간을 허용하는 명령에 대한 공통 설정자를 제공합니다(예
public:
void setKey(const std::문자열 &s) {
cmd.v.v0.키 = s.c_str();
cmd.v.v0.nkey = s.크기();
}
// …
비공개:
T cmd;
};
응답 개체
명령 객체와 마찬가지로 응답 객체도 계층 구조로 제공되며, 유일한 데이터 멤버로 C lcb_resp_t *를 포함합니다. 계층 구조는 다음과 같습니다:
- 초록 응답베이스 클래스. 여기에는 키에 대한 접근자가 있습니다.
- 초록 CasResponseBase 클래스를 상속하는 응답베이스 CAS에 대한 액세서리를 제공합니다.
- 그리고 응답
T::v.v0.key에서 키 정보를 검색하는 ResponseBase를 구현하는 클래스입니다. - 그리고 CasResponse
에서 상속되는 응답를 통해 cas를 제공하여 CasResponseBase를 구현합니다. T::v.v0.cas - 추가 정보를 제공하는 응답별 클래스(예. GetResponse 는 다음과 같이 구현됩니다. CasResponse
값과 플래그에 대한 추가 접근자가 있습니다.
헤더에서는 다음과 같이 표시됩니다:
public:
가상 const void *getKey(lcb_size_t *n) const = 0;
std::문자열 getKey() const;
};
클래스 응답 : 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;
};
클래스 CasResponse : public 응답<T, CasResponseBase>
{
public:
가상 lcb_cas_t getCas() const {
반환 응답<T,CasResponseBase>::getRawResponse()–>v.v0.cas;
}
};
public:
lcb_uint64_t getValue() const { 반환 getRawResponse()–>v.v0.값; }
};
응답 클래스는 템플릿과 순수한 추상 베이스에서 파생되므로 공통 멤버를 헬퍼 메서드와 클래스로 처리할 수 있으므로, 예를 들어 응답에서 키를 기록하는 logKey라는 함수의 경우 다음과 같이 구현할 수 있습니다:
std::cout << resp–>getKey() << std::endl;
}
그런 다음 모든 응답 객체와 함께 로그키를 호출할 수 있습니다.
콜백
마지막으로 콜백 인터페이스도 구현했습니다. libcouchbase는 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"
를 클릭한 다음 콜백을 설정하려면
새로운 바인딩에서는 콜백이 통합된 객체에 노출되므로 핸들러를 명시적으로 설정할 필요 없이 ResponseHandler 클래스를 서브클래싱하고 onArithmetic(OperationContext *, const ArithmeticResponse *, lcb_error_t) 메서드를 사용합니다.
일반 명령에 대한 전용 핸들러를 구현하고 싶지 않다면 간단히 onDefault(OperationContext *, const ResponseBase *, lcb_error_t) 를 클릭하고 거기에서 모든 명령을 처리합니다.
[...] 금주의 블로그 포스트 #2 : C++와 스레드가 있는 libcouchbase (1/2)금주의 질문 : 스왑 리밸런싱의 시간 비용에 대한 질문 [...]