我正在写一个围绕C库的C++封装器。下面是我的策略的一个例子。
// header file
class LibrdfUri { // wrapper around librdf.h librdf_uri*
/*
* If the deleter of std::unique_ptr is an empty
* class then it can do some optimizations and
* not actually store the deleter object.
* Otherwise it has to accommodate extra space for
* the deleter, which is unnecessary
* https://stackoverflow.com/questions/61969200/what-is-the-purpose-of-wrapping-this-private-deleter-function-in-a-struct/61969274#61969274
*/
struct deleter {
// turns deleter into a functor. For passing on to unique_ptr
void operator()(librdf_uri *ptr);
};
// automate management of librdf_uri* lifetime
std::unique_ptr<librdf_uri, deleter> librdf_uri_;
public:
LibrdfUri() = default;
explicit LibrdfUri(const std::string& uri); // construct from string
librdf_uri *get(); // returns the underlying raw pointer
};
// implementation
void LibrdfUri::deleter::operator()(librdf_uri *ptr) {
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
LibrdfUri::LibrdfUri(const std::string &uri) {
// create pointer to underlying C library 'object'
librdf_uri_ = std::unique_ptr<librdf_uri, deleter>(
librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()) // World::getWorld is static. Returns a pointer required by librdf_new_uri
);
}
librdf_uri *LibrdfUri::get() {
return librdf_uri_.get();
}
// and is used like so:
LibrdfUri uri("http://uri.com");
librdf_uri* curi = uri.get(); // when needed
这适用于单一类型的 librdf_uri*
是底层库的一部分,然而我有很多这样的库。我的问题是双管齐下的。第一部分是关于将这个封装器泛化到其他类的最佳通用策略,而第二部分是关于该策略的实现。
关于第一部分,我的想法是这样的。 1. 我可以像我在这里做的那样,手动实现每个类。这可能是最简单、最不优雅的方法。然而它仍然可能是我最好的选择。然而这涉及到少量的代码重复,因为我写的每个CWrapper基本上都会有相同的结构。更不用说如果我需要改变一些东西,那么我就必须单独做每个类。 2. 使用一个基类(抽象的?) 3. 使用一个模板
我的问题的第二部分基本上是:如果我实现了方案2或方案3(我认为甚至可能只是一个单一的方案),我将如何做?
这是我所想的一个(大大的破绽)版本。
template<class LibrdfType>
class CWrapper {
struct deleter { ; //?
void operator()(LibrdfType *ptr) {
// ??
};
}
std::unique_ptr<LibrdfType, deleter> ptr;
public:
CWrapper() = default;
LibrdfType *get() {
ptr.get();
};
};
那么.., LibrdfUri
和其他我需要封装的C类,只需将其子类化为 CWrapper
这是个比较好的删除器。
template<auto f>
using deleter=std::integral_constant< std::decay_t<decltype(f)>, f >;
这是个更好的删除器:使用:
deleter<librdf_free_uri>
是一个无状态的删除器,它调用 librdf_free_uri
.
但我想我们不需要这样做 我的做法是这样的
你需要三条信息:
一种方法是用著名的名字定义ADL基础助手,然后覆盖到deleteconstruct。
template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag{};
template<class T>
void delete_wrapptr(T*)=delete;
struct cleanup_wrapptr{
template<class T>
void operator()(T* t)const{ delete_wrapptr(t); }
};
template<class T>
using wrapptr=std::unique_ptr<T, cleanup_wrapptr>;
template<class T>
wrapptr<T> make_wrapptr( tag_t<T>, ... )=delete;
现在你只需要为make和delete写重载。
void delete_wrapptr(librdf_uri* ptr){
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
librdr_uri* make_wrapptr(tag_t<librdf_uri>, const std::string &uri) {
return librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()); // World::getWorld is static. Returns a pointer required by librdf_new_uri
}
而且你可以。
wrapptr<librdf_uri> ptr = make_wrapptr(tag<librdf_uri>, uri);
实现就变成了重载这两个函数。
make_wrapptr
和 delete_wrapptr
你写的重载需要在创建点可见,并且在命名空间的 T
, tag_t
或 cleanup_wrapptr
. 实现可以隐藏在cpp文件中,但重载的声明不能。