使用boost的shared_ptr管理C类型生命周期?

发布于 2025-01-05 23:14:01 字数 1217 浏览 0 评论 0原文

我有一个类似于 如何管理对象的问题使用 Boost 库智能指针的生命周期?但是,就我而言,“对象”根本不是 C++ 对象,而是从 C API 返回/传递的不透明类型。该类型没有指针语义,即没有解引用;然而,它作为参数传递给 C API 中的其他函数。该类型还有一个明确的 close API,必须调用该 API 才能清理内部资源。

因此,我有一个 C API,它类似于出于

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

各种原因,在我的 C++ 代码中,我想管理 opaque_legacy_type_t 的“实例”,就像管理堆分配的对象实例一样,即具有与 boost::shared_ptr。似乎 shared_ptr 提供了足够的功能,我可以通过这样做来管理调用 XXclose

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

但是,由于 opaque_legacy_type_t 没有指针语义,因此用法托管有点笨拙。

我想做的是拥有类似于 shared_ptrmanagement_type 之类的东西,并且正在寻找不需要我全部编写的想法。

编辑: 我纠正了示例中最初的错误。旧版 API 通过值而不是指针获取不透明类型。

I have a question similar to How to manage object life time using Boost library smart pointers? but, in my case, the "object" isn't a C++ object at all, but an opaque type returned/passed out from a C API. The type does not have pointer semantics, i.e., there is no dereferencing; it is, however, passed as an argument to other functions in the C API. The type also has a definitive close API which must be called in order to clean up internal resources.

So, I have a C API that's something along the lines of

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

For various reasons, in my C++ code I would like to manage "instances" of opaque_legacy_type_t much like I would manage heap-allocated object instances, i.e. with similar sharing semantics as boost::shared_ptr<>. It seems that shared_ptr offers enough that I can manage calling XXclose by doing this:

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

But, since opaque_legacy_type_t doesn't have pointer semantics, the usage of managed is a bit clumsy.

What I'd like to do is have something like a managed_type that is similar to shared_ptr, and am looking for ideas that don't require me to write it all.

EDIT: I corrected my original screw-up in the example. The legacy API takes the opaque type by value rather than by pointer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

滥情空心 2025-01-12 23:14:01

由于所有旧版 API 都采用指向不透明类型的指针,因此您可以直接使用共享指针。关键是您不要在堆栈上声明原始结构,而是通过 new 分配它:

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(x.get(), ...);
}


EDIT: If some API take the opaque type by value instead of pointer, then pass the dereferenced pointer.

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(*p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(*x, ...);
}

Since all of the legacy API take a pointer to the opaque type, you could use shared pointers directly. The key is for you to not declare the original structure on the stack, but rather allocate it via new:

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(x.get(), ...);
}


EDIT: If some API take the opaque type by value instead of pointer, then pass the dereferenced pointer.

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(*p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(*x, ...);
}
久隐师 2025-01-12 23:14:01

您可以将 boost 智能指针与 pimpl idom 一起使用:

class shared_opaque_legacy_type_t {
    struct impl {
        opaque_legacy_type_t t;
        impl(...) { XXOpen(..., t); }
        ~impl(...) { XXClose(t); }
    }
    boost::shared_ptr<impl> _impl;
public:
    shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}

    opaque_legacy_type_t* get() {
        return _impl->t;
    }
};


shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);

缺点是您仍然可以调用 XXclose(x.get()) 并使您的对象无效。

更新:修复了它。 :-)

You could use boost smart pointers together with the pimpl idom:

class shared_opaque_legacy_type_t {
    struct impl {
        opaque_legacy_type_t t;
        impl(...) { XXOpen(..., t); }
        ~impl(...) { XXClose(t); }
    }
    boost::shared_ptr<impl> _impl;
public:
    shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}

    opaque_legacy_type_t* get() {
        return _impl->t;
    }
};


shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);

The drawback is that you could still call XXclose(x.get()) and invalidate your object.

UPDATE: Fixed it. :-)

治碍 2025-01-12 23:14:01

您可以编写一个与 boost 一起使用的包装器,它将调用 ctor 中的 open() 和 dtor 中的 close()

You could write a wrapper to use with boost that will call the open() in the ctor and the close() in the dtor.

像极了他 2025-01-12 23:14:01

我投票支持 Rob 的答案,该答案仅使用没有包装器的 shared_ptr ,但如果您确实想避免动态分配,这里有一个简单的小示例说明如何做到这一点。

它是一个直接持有句柄并且不进行分配的模板。您向构造函数传递一个用于创建不透明类型的对象的函子,以及一个在需要销毁该类型时调用的删除器。它是可移动且不可复制的,因此现在需要共享引用计数。它实现了隐式转换运算符,因此您可以在使用保留类型的值的地方使用它。

template<typename T,typename D>
class opaque_type_handle {
    T handle;
    D deleter;
    bool needs_delete;
public:
    template<typename F>
    opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}

    opaque_type_handle(opaque_type_handle const &) = delete;
    opaque_type_handle &operator=(opaque_type_handle const &) = delete;

    opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
        rhs.needs_delete = false;
    }
    opaque_type_handle &operator=(opaque_type_handle &&rhs) {
        handle = rhs.handle;
        deleter = rhs.deleter;
        needs_delete = true;
        rhs.needs_delete = false;
        returh *this;
    }

    ~opaque_type_handle() {
        if(needs_delete) {
            deleter(handle);
        }
    }

    operator T&() { return handle; }
    operator T() const { return handle; }
};

像这样使用它:

// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;

legacy_handle make_legacy_handle(...) {
    return legacy_handle(
        [](){
            opaque_legacy_type_t tmp;
            XXopen(..., &tmp);
            return tmp;
        },
        &XXclose
    );
}

legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);

I voted for Rob's answer that just uses a shared_ptr with no wrapper, but if you really want to avoid dynamic allocation here's a simple little example of how to do that.

It's a template that directly holds the handle and does no allocation. You pass the constructor a functor that creates an object of the opaque type, and a deleter to call when the type needs to be destroyed. It's movable and non-copyable so now shared reference count is needed. It implements implicit conversion operators so you can use it where you'd use a value of the held type.

template<typename T,typename D>
class opaque_type_handle {
    T handle;
    D deleter;
    bool needs_delete;
public:
    template<typename F>
    opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}

    opaque_type_handle(opaque_type_handle const &) = delete;
    opaque_type_handle &operator=(opaque_type_handle const &) = delete;

    opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
        rhs.needs_delete = false;
    }
    opaque_type_handle &operator=(opaque_type_handle &&rhs) {
        handle = rhs.handle;
        deleter = rhs.deleter;
        needs_delete = true;
        rhs.needs_delete = false;
        returh *this;
    }

    ~opaque_type_handle() {
        if(needs_delete) {
            deleter(handle);
        }
    }

    operator T&() { return handle; }
    operator T() const { return handle; }
};

Use it like so:

// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;

legacy_handle make_legacy_handle(...) {
    return legacy_handle(
        [](){
            opaque_legacy_type_t tmp;
            XXopen(..., &tmp);
            return tmp;
        },
        &XXclose
    );
}

legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文