如何围绕一个C "类 "泛化这个C++包装器?

问题描述 投票:1回答:1

我正在写一个围绕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

c++ c templates inheritance smart-pointers
1个回答
1
投票

这是个比较好的删除器。

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

© www.soinside.com 2019 - 2024. All rights reserved.