删除 boost::bind 的原始指针参数

发布于 2024-11-06 20:02:35 字数 1216 浏览 3 评论 0原文

假设我已经分配了堆A*,我想将其作为参数传递给boost::bindboost::bind 被保存在一些 STL 中(例如 boost::functions 的容器)以便以后处理。

我想确保 A* 将在 STL 容器销毁时被销毁。

演示:

A* pA = new A();

// some time later
container.push_back(boost::bind(&SomeClass::HandleA, this, pA);

// some time later
container is destroyed => pA is destroyed too

如何才能做到?

编辑

也许我想要的并不那么现实。

我有原始指针和接收原始指针的函数。调用通过boost::bind的方式进行延迟。此时我想要自动内存管理,以防 boost::bind 想要执行。我很懒,所以我想使用“现成的”智能指针解决方案。

std::auto_ptr 看起来是一个不错的候选者,但是......

auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, pAutoA);

无法编译(请参阅此处)

auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA));

pAutoA 被销毁,删除底层 pA。

编辑02

在提到的容器中,我需要存储具有不同参数的杂项“回调”。其中一些是指向对象的原始指针。由于代码很旧,我并不总是可以更改它。

编写自己的包装器来在容器中存储回调是最后的手段(虽然可能是唯一的手段),因此是赏金。

Lets say I have heap allocated A*, which I want to pass as argument to boost::bind.
boost::bind is saved for later processing in some STL like container of boost::functions's.

I want to ensure A* will be destroyed at destruction of the STL container.

To demostrate:

A* pA = new A();

// some time later
container.push_back(boost::bind(&SomeClass::HandleA, this, pA);

// some time later
container is destroyed => pA is destroyed too

How can it be done?

EDIT

Maybe what I want is not that realistic.

I have raw pointer and function which receives the raw pointer. The call is delayed by means of boost::bind. At this point I want automatic memory management in case boost::bind want executed. I'm lazy, so I want to use "ready" smart-pointer solution.

std::auto_ptr looks like a good candidate, however ...

auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, pAutoA);

doesn't compile (see here)

auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA));

pAutoA is destroyed, deleting underlying pA.

EDIT 02

In the mentioned container I will need to store misc "callbacks" with different arguments. Some of them are raw pointers to object. Since the code is old, I not always can change it.

Writing own wrapper for storing callbacks in container is last resort (while maybe the only one), hence bounty.

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

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

发布评论

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

评论(4

灼疼热情 2024-11-13 20:02:35

@pmjordan 的想法已经在朝着正确的方向发展。您回答说您不能使用 shared_ptr,因为一旦构建您就无法收回其所有权。但这并不完全正确:使用 shared_ptr 的自定义删除器机制,您可以。方法如下:

为您的 Af(A*) 假设这些玩具定义:

struct A {
    ~A() { std::cout << "~A()" << std::endl; }
};

void f( A * a ) {
    std::cout << "in f(A*)" << std::endl;
    delete a;
}
  1. 编写一个可以“关闭”的删除器:

    struct opt_delete {
        布尔m_删除;
        opt_delete() : m_delete( true ) {}
        模板 <类型名称 T>
        无效运算符()(T * t){
            if ( m_delete ) 删除 t;
        }
    };
    
  2. 然后您可以编写一个 take() 函数来获取 shared_ptr 的所有权> 再次有效负载:

    模板 <类型名称 T>
    T * take( const boost::shared_ptr & sp ) {
        opt_delete * d = boost::get_deleter( sp );
        断言(d);
        断言( d->m_delete == true );
        d->m_delete = false;
        返回 sp.get();
    }
    

    (这会将有效负载保留在剩余的 shared_ptr 实例中,但对于您的情况,这是可以的,并且 assert() 涵盖了不适用的情况) .

  3. 现在您可以像这样手动包装f(A*)

    void f_sp( const boost::shared_ptr & a ) {
        f(取(a));
    }
    
  4. 最后,测试这两个场景:

    int main( int argc, char * argv[] ) {
    
        const boost::shared_ptr; a( 新 A, opt_delete() );
    
        const boost::function;函数=
            boost::bind( &f_sp, a );
    
        if ( argc >= 2 && *argv[1] == '1' ) // 调用 'func'
            函数();
        别的
            ; // 不
    
        返回0;
    }
    

使用 1 参数执行测试程序将打印

在 f(A*) 中
〜A()

且没有(或任何其他参数),它将打印

~A()

您可以扩展测试工具以首先将 func 放入容器中,但它仍然是安全的。在这种情况下,唯一不安全的是多次调用 func 副本(但随后您将触发 take() 中的第二个断言)。

编辑:请注意,此机制不是线程安全的。为了使其线程安全,您需要为 opt_delete 提供互斥体,以将 operator()take() 同步。

The idea of @pmjordan was already going in the right direction. You replied that you can't use shared_ptr, because you can't take ownership back from it once constructed. But that is not entirely correct: with shared_ptr's custom deleter mechanism, you can. This is how:

Assume these toy defintions for your A and f(A*):

struct A {
    ~A() { std::cout << "~A()" << std::endl; }
};

void f( A * a ) {
    std::cout << "in f(A*)" << std::endl;
    delete a;
}
  1. Write a deleter that can be "switched off":

    struct opt_delete {
        bool m_delete;
        opt_delete() : m_delete( true ) {}
        template <typename T>
        void operator()( T * t ) {
            if ( m_delete ) delete t;
        }
    };
    
  2. Then you can write a take() function that takes ownership of the shared_ptr payload again:

    template <typename T>
    T * take( const boost::shared_ptr<T> & sp ) {
        opt_delete * d = boost::get_deleter<opt_delete>( sp );
        assert( d );
        assert( d->m_delete == true );
        d->m_delete = false;
        return sp.get();
    }
    

    (this will leave the payload in the remaining shared_ptr instances, but for your case, that's ok, and the assert()s cover the cases when it's not).

  3. Now you can manually wrap f(A*) like this:

    void f_sp( const boost::shared_ptr<A> & a ) {
        f( take( a ) );
    }
    
  4. And finally, test the two scenarios:

    int main( int argc, char * argv[] ) {
    
        const boost::shared_ptr<A> a( new A, opt_delete() );
    
        const boost::function<void()> func =
            boost::bind( &f_sp, a );
    
        if ( argc >= 2 && *argv[1] == '1' ) // call 'func'
            func();
        else
            ; // don't
    
        return 0;
    }
    

Executing the test program with a 1 argument will print

in f(A*)
~A()

and without (or any other argument), it will print

~A()

You can extend the test harness to put func into a container first, but it'll still be safe. The only thing that isn't safe in the case is calling the func copies more than once (but then you'll trigger the second assertion in take()).

EDIT: Note that this mechanism isn't thread-safe. To make it thread-safe, you need to supply opt_delete with a mutex to synchronise operator() with take().

素食主义者 2024-11-13 20:02:35

我假设你的意思是你有一些函数,我们称之为 f() ,它需要一个 A* ,然后你用 boost::bind 代理它>?您可以更改此函数以接受 Boost/TR1 shared_ptr 吗?使用 shared_ptr (或者不太可能,C++98 std::auto_ptr)应该可以解决您的生命周期问题。

或者,如果您无法更改 f 本身,则可以创建一个包装器,它接受 shared_ptr,提取原始指针并调用 f< /代码> 与它。如果您发现自己编写了很多这样的包装器,则假设函数签名相似,您也许可以创建一个模板来生成它们。

I assume you mean you have some function, let's call it f() which takes an A*, which you then proxy with boost::bind? Can you change this function to accept a Boost/TR1 shared_ptr<A> instead? Using a shared_ptr (or, less likely, a C++98 std::auto_ptr) should solve your lifecycle problem.

Alternatively, if you can't change f itself, you could create a wrapper which accepts a shared_ptr<A>, pulls out the raw pointer and calls f with it. If you find yourself writing a lot of these wrappers, you may be able to create a template for generating them, assuming the function signatures are similar.

风铃鹿 2024-11-13 20:02:35

注意!这太丑了!

刚刚完成了一些概念验证。嗯,据我所知,它按照要求做了 - 但这个东西依赖于 const_cast 假设。如果您决定在程序中使用类似的东西,请准备好仔细检查程序中始终发生的所有复制结构,并使用 valgrind 来验证没有任何泄漏/损坏。

技巧在于定义您自己的包装类,它忽略 const 限定符并允许 auto_ptr 所有权从 const 引用的 auto_ptr 转移。例如,如果您尝试复制向量本身,这可能会变得疯狂。

因此,请务必仔细阅读向量复制语义、auto_ptr 所有权转移语义,最重要的是 - 只需使用 shared_ptr :)

#include <iostream>
#include <boost/bind.hpp>
#include <algorithm>
#include <vector>
#include <boost/function.hpp>

class parameter_data
{
    public:
    ~parameter_data()
    {
        std::cout << "~parameter_data()" << std::endl;
    }

    parameter_data()
    {
        std::cout << "parameter_data()" << std::endl;
    }
};

void f( parameter_data* data )
{
    std::cout << "Processing data..." << std::endl;
};


class storage_wrapper
{
    private:
        boost::function<void()> callable;
        std::auto_ptr<parameter_data> data;
    public:
        storage_wrapper( const storage_wrapper& copy ) 
        {
            callable = const_cast< storage_wrapper&>(copy).callable;
            data = const_cast< storage_wrapper&>(copy).data;
        }

        storage_wrapper( parameter_data *adata )
            : data( adata )
        {
            callable = boost::bind( &f, adata );
        }

        storage_wrapper& operator=( const storage_wrapper& copy)
        {
            callable = const_cast< storage_wrapper&>(copy).callable;
            data = const_cast< storage_wrapper&>(copy).data;
        }

        void operator()()
        {
            callable();
        }
};

int main()
{
    std::cout << "Start of program" << std::endl;
    {
        std::vector<storage_wrapper> container;
        for ( int i = 0; i < 100; i++ )
            container.push_back( storage_wrapper( new parameter_data() ) );
        for ( int i = 0; i < 100; i++ )
            container[i]();
    }
    std::cout << "End of program" << std::endl;
    return 0;
}

NB! This is UGLY!

Have just scrateched some proof of concept. Well, it does what requested, as far as I can see - but this stuff relies on const_cast assumption. If you decide to use something like that in your program, be ready to double check all copy constructions happening in your program all the time, and using valgrind to verify nothing is leaked/corrupted.

Trick is in defining you own wrapper class, that ignores const qualifiers and allows auto_ptr ownership transfer from const referenced auto_ptr. This can get crazy if you ll try, for example, copy vector itself.

So be sure to read carefuly about vector copy semantics, auto_ptr ownership transfer semantics and, best of all - just use shared_ptr :)

#include <iostream>
#include <boost/bind.hpp>
#include <algorithm>
#include <vector>
#include <boost/function.hpp>

class parameter_data
{
    public:
    ~parameter_data()
    {
        std::cout << "~parameter_data()" << std::endl;
    }

    parameter_data()
    {
        std::cout << "parameter_data()" << std::endl;
    }
};

void f( parameter_data* data )
{
    std::cout << "Processing data..." << std::endl;
};


class storage_wrapper
{
    private:
        boost::function<void()> callable;
        std::auto_ptr<parameter_data> data;
    public:
        storage_wrapper( const storage_wrapper& copy ) 
        {
            callable = const_cast< storage_wrapper&>(copy).callable;
            data = const_cast< storage_wrapper&>(copy).data;
        }

        storage_wrapper( parameter_data *adata )
            : data( adata )
        {
            callable = boost::bind( &f, adata );
        }

        storage_wrapper& operator=( const storage_wrapper& copy)
        {
            callable = const_cast< storage_wrapper&>(copy).callable;
            data = const_cast< storage_wrapper&>(copy).data;
        }

        void operator()()
        {
            callable();
        }
};

int main()
{
    std::cout << "Start of program" << std::endl;
    {
        std::vector<storage_wrapper> container;
        for ( int i = 0; i < 100; i++ )
            container.push_back( storage_wrapper( new parameter_data() ) );
        for ( int i = 0; i < 100; i++ )
            container[i]();
    }
    std::cout << "End of program" << std::endl;
    return 0;
}
软糯酥胸 2024-11-13 20:02:35

它不需要非常复杂:

class MyContainer : public std::vector<boost::function<void ()> > {
public:
   void push_back(boost::function<void ()> f, A *pA) 
       { push_back(f); vec.push_back(pA); }
   ~MyContainer() 
       { int s=vec.size; for(int i=0;i<s;i++) delete vec[i]; }
private:
   std::vector<A*> vec;
};

它有一个问题,您需要通过 MyContainer & 将它传递给其他函数。而不是 std::vector 引用,否则可以调用原始的push_back,并且它允许您可以在不提供 A* 指针的情况下push_back。此外,它不会检查绑定参数是否与 pA 是相同的 A* 对象。您可以通过更改push_back原型来解决这个问题:

template<class T>
void push_back(T *object, void (T::*fptr)(), A *pA) 
{
   push_back(boost::bind(fptr, object, pA)); vec.push_back(pA);
} 

It doesn't need to be very complex:

class MyContainer : public std::vector<boost::function<void ()> > {
public:
   void push_back(boost::function<void ()> f, A *pA) 
       { push_back(f); vec.push_back(pA); }
   ~MyContainer() 
       { int s=vec.size; for(int i=0;i<s;i++) delete vec[i]; }
private:
   std::vector<A*> vec;
};

It has one problem that you need to pass it to other functions via MyContainer & instead of std::vector reference, otherwise the original push_back can be called and it allows for cases where you can push_back without providing the A* pointer. Also it has no check for bind parameters to be the same A* object than pA. You can fix that by changing the push_back prototype:

template<class T>
void push_back(T *object, void (T::*fptr)(), A *pA) 
{
   push_back(boost::bind(fptr, object, pA)); vec.push_back(pA);
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文