使用时删除升压功能

发布于 2024-08-02 00:37:50 字数 496 浏览 2 评论 0 原文

我遇到的情况是 boost::function 和 boost::bind (实际上是 std::tr1::function 和 bind)在使用时被删除。 这安全吗? 我通常会避免它,但有问题的代码有点根深蒂固,我唯一的其他选择是添加一个新线程。

typedef function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
     Magic m;
    global_foo = new foo_type( bind( &actual_foo, _1, &m );

    return (*global_foo)(10)
}

绑定参数始终是普通整型(实际代码中的 int 和指针),而不是引用。

I have a situation where a boost::function and boost::bind (actually a std::tr1::function and bind) are being deleted while still in use. Is this safe? I would normally avoid it, but the offending code is a bit entrenched and my only other option is adding a new thread.

typedef function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
     Magic m;
    global_foo = new foo_type( bind( &actual_foo, _1, &m );

    return (*global_foo)(10)
}

The bound parameters are always plain integral types (an int and a pointer in the real code), and not references.

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

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

发布评论

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

评论(2

故人的歌 2024-08-09 00:37:50

boost::functionstd::tr1::functions 是可复制对象。 因此,通常绝对没有理由分配它们——只需按值传递它们即可。

它们针对大​​多数实际情况进行了很好的优化...所以只需按值传递它们:

typedef function<int(int)> foo_type;

foo_type global_foo;

int actual_foo( int i, Magic* m )
{
   delete global_foo;
   return m->magic(i);
}

int main()
{
   Magic m;
   global_foo = bind( &actual_foo, _1, &m );

   return global_foo(10)
}

您建议的代码是危险的,运行此代码:

    #include <boost/function.hpp>
    #include <boost/bind.hpp>
    #include <iostream>
    using namespace std;

    boost::function<void()> *glb;

    struct Data {
            int x;
            Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; }
            ~Data() { cout<<"dtor:"<<this<<endl; }
            Data(Data const &p) {x=p.x; cout<<"ctor:"<<this<<endl; }
            Data const &operator=(Data const &p) { x=p.x; cout<<this<<"="<<&p<<endl; return *this; }
    };

    void func(Data const &x)
    {
            delete glb;
            cout<<&x<<endl;
    }

    int main()
    {
            glb=new boost::function<void()>(boost::bind(func,Data(3)));

            (*glb)();
            return 0;
    }

您会发现您尝试在 func 中访问已销毁的对象cout<<&x< 中显示的(具有相同指针值的 dtor)已被调用。

因为当你销毁函数对象时,你也销毁了绑定的参数
have 和 Data const &x 变得不可用,因为它已被 global_function 破坏

编辑: 注释说明:

如果您有类似的内容

map<string,function<void()> > calls;

void delete_key(){
    calls.erase("key");
}

main() 
{
     calls["key"]=delete_key;

     // Wrong and dangerous
     // You delete function while in use
     calls["key"]();

     // Correct and safe
     // You create a copy of function so it would not
     // be deleted while in use.
     function<void()> f=calls["key"];
     f();
}

boost::function or std::tr1::functions are copyable objects. So, generally there is absolutly no reason to allocate them -- just pass them by value.

They are well optimized for most of real cases... So just pass them by value:

typedef function<int(int)> foo_type;

foo_type global_foo;

int actual_foo( int i, Magic* m )
{
   delete global_foo;
   return m->magic(i);
}

int main()
{
   Magic m;
   global_foo = bind( &actual_foo, _1, &m );

   return global_foo(10)
}

The code you suggested is dangerous, run this code:

    #include <boost/function.hpp>
    #include <boost/bind.hpp>
    #include <iostream>
    using namespace std;

    boost::function<void()> *glb;

    struct Data {
            int x;
            Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; }
            ~Data() { cout<<"dtor:"<<this<<endl; }
            Data(Data const &p) {x=p.x; cout<<"ctor:"<<this<<endl; }
            Data const &operator=(Data const &p) { x=p.x; cout<<this<<"="<<&p<<endl; return *this; }
    };

    void func(Data const &x)
    {
            delete glb;
            cout<<&x<<endl;
    }

    int main()
    {
            glb=new boost::function<void()>(boost::bind(func,Data(3)));

            (*glb)();
            return 0;
    }

You would find that you try to access in func to destoryed object (dtor with same pointer value) that is shown in cout<<&x<<endl was already called.

Because when you destroy your function objects you also destroy the binded parameters you
have and Data const &x become unavailible becuase it was destoryed with global_function

Edit: Clearification for comment:

If you have something like

map<string,function<void()> > calls;

void delete_key(){
    calls.erase("key");
}

main() 
{
     calls["key"]=delete_key;

     // Wrong and dangerous
     // You delete function while in use
     calls["key"]();

     // Correct and safe
     // You create a copy of function so it would not
     // be deleted while in use.
     function<void()> f=calls["key"];
     f();
}
心是晴朗的。 2024-08-09 00:37:50

编辑:(与 Charles Bailey)进一步讨论了这个答案,我认为这是不安全的,因为它取决于 boost::function 的实现。

当我们进入actual_foo时,调用堆栈将如下所示:

actual_foo
boost::function::operator()
main

因此,当actual_foo执行完成时,我们将跳回到boost::function的operator()代码,并且该对象已被删除。 这并不保证会成为问题 - 这有点像调用 delete this - 但您必须非常小心在已删除对象的成员函数中执行的操作。 您不得调用任何虚拟函数或使用任何数据成员。

问题是我不知道 boost::function 在调用它所包装的函数之后对它在 operator() 中执行的操作做出任何保证。 在我的平台上,它似乎没有做任何危险的事情(所以 valgrind 不会抱怨),但完全可以想象,在不同的平台上,使用不同的实现,或者使用不同的编译标志,它可能想做一些不一旦对象被删除就不允许 - 例如它可能使用成员变量写出一些调试信息。

因此,我认为这是一件危险的事情,在某些情况下可能会导致未定义的行为。

进一步说明:

我快速浏览了一下 boost 在调用函数指针后到底做了什么。 看这里: http://boost .cvs.sourceforge.net/viewvc/boost/boost/boost/function/function_template.hpp?view=markup 在第687行的operator()函数处,我的解释是它立即返回返回值并且不不做任何其他事情,所以在实践中,通过这种实现,你应该没问题,但关于它是危险的评论仍然存在。 请注意,我很可能发现了错误的代码和/或理解错误......

下面的原始答案:

我相信这是可以的。 当您输入 actual_foo 时,boost::bindboost::function 对象已经完成了它们的工作,并且您正在执行真正的函数actual_foo。

我通过 valgrind 运行程序,在我的平台(gcc 4.2.4、Linux)上检查了这一点。

这是我运行的程序:

#include <boost/bind.hpp>
#include <boost/function.hpp>

class Magic
{
public:
    int magic( int i )
    {
        return 5;
    }
};

typedef boost::function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
    Magic m;
    global_foo = new foo_type( boost::bind( &actual_foo, _1, &m ) );

    return (*global_foo)(10);
}

这是 valgrind 的输出:

$ valgrind ./boost_bind
==17606== Memcheck, a memory error detector.
==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==17606== Using LibVEX rev 1804, a library for dynamic binary translation.
==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework.
==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==17606== For more details, rerun with: -v
==17606== 
==17606== 
==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1)
==17606== malloc/free: in use at exit: 0 bytes in 0 blocks.
==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated.
==17606== For counts of detected errors, rerun with: -v
==17606== All heap blocks were freed -- no leaks are possible.

不过,我必须说,这似乎是一件奇怪的事情。 如果可能的话,我更愿意通过将其设为堆栈变量来自动删除此函数对象,或者在堆栈变量的析构函数中删除它(如 RAII)。 它会更整洁、更安全、不那么可怕,而且更安全。 但我确信您已经了解了所有这些,并且有充分的理由按原样使用代码。

Edit: Having discussed this answer a little more (with Charles Bailey), I believe this is unsafe since it depends on the implementation of boost::function.

The call stack when we enter actual_foo will look something like this:

actual_foo
boost::function::operator()
main

So when actual_foo has finished executing, we will jump back into the operator() code of boost::function, and this object has been deleted. This is not guaranteed to be a problem - it's a bit like calling delete this - but you have to be very careful what you do in a member function of a deleted object. You're not allowed to call any virtual functions or use any data members.

The problem is that I am not aware that boost::function makes any guarantees about what it does in operator() after the function it wraps has been called. It seems on my platform it doesn't do anything dangerous (so valgrind doesn't complain) but it is perfectly conceivable that on a different platform, with a different implementation, or with different compile flags, it might want to do something that isn't allowed once the object has been deleted - for example it might write out some debug information using a member variable.

Thus I believe this is a dangerous thing to do that might cause undefined behaviour under certain circumstances.

Further Note:

I took a quick look at what boost is really doing after it calls the function pointer. Looking here: http://boost.cvs.sourceforge.net/viewvc/boost/boost/boost/function/function_template.hpp?view=markup at the operator() function on line 687, my interpretation is that it immediately returns the return value and doesn't do anything else, so in practice, with that implementation, you should be ok, but the comments about it be dangerous still hold. Note that I may very well have found the wrong bit of code and/or understood it wrong...

Original answer below:

I believe this is ok. By the time you enter actual_foo, the boost::bind and boost::function objects have finished their jobs, and you are executing the real function actual_foo.

I checked this out on my platform (gcc 4.2.4, Linux) by running the program through valgrind.

Here is the program I ran:

#include <boost/bind.hpp>
#include <boost/function.hpp>

class Magic
{
public:
    int magic( int i )
    {
        return 5;
    }
};

typedef boost::function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
    Magic m;
    global_foo = new foo_type( boost::bind( &actual_foo, _1, &m ) );

    return (*global_foo)(10);
}

and here is the valgrind output:

$ valgrind ./boost_bind
==17606== Memcheck, a memory error detector.
==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==17606== Using LibVEX rev 1804, a library for dynamic binary translation.
==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework.
==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==17606== For more details, rerun with: -v
==17606== 
==17606== 
==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1)
==17606== malloc/free: in use at exit: 0 bytes in 0 blocks.
==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated.
==17606== For counts of detected errors, rerun with: -v
==17606== All heap blocks were freed -- no leaks are possible.

I must say, though, that this seems a strange thing to do. I would much prefer, if it were possible, to use automatic deletion of this function object by making it a stack variable, or deleting it in the destructor of a stack variable (as in RAII). It would be neater, safer, less scary, and more exception-safe. But I'm sure you already know all that, and there are good reasons for living with the code as it is.

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