如何围绕 C 'class' 概括此 C++ 包装器?

How to generalize this C++ wrapper around a C 'class'?

我正在围绕 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
         *  
         */
        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*,它是基础库的一部分,但我有很多这样的类型。我的问题是双重的。第一部分涉及将此包装器推广到其他 classes 的最佳通用策略,而第二部分涉及该策略的实施。

关于第一部分,我的想法是: 1. 我可以像这里所做的那样手动实现每个 class。这可能是最简单和最不优雅的。但它仍然可能是我最好的选择。然而,其中涉及少量代码重复,因为我编写的每个 CWrapper 基本上都具有相同的结构。更不用说如果我需要更改某些内容,那么我将不得不单独执行每个 class。 2.使用基数class(抽象?) 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 class,只需 subclass CWrapper

这是一个更好的删除器:

template<auto f>
using deleter=std::integral_constant< std::decay_t<decltype(f)>, f >;

使用:

deleter<librdf_free_uri>

是调用librdf_free_uri的无状态删除器。

但我认为我们不需要。这是我可能会做的:

您需要3条信息。

  • 如何构造
  • 如何销毁
  • 存储什么类型

一种方法是定义具有著名名称的 ADL baser 助手,您可以将其覆盖为 delete/construct。

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_wrapptrdelete_wrapptr 重载需要在创建点以及 Ttag_tcleanup_wrapptr 的命名空间中可见。实现可以隐藏在cpp文件中,但重载的声明不能。