使用任意签名委托boost :: function

时间:2013-12-14 01:12:24

标签: c++ metaprogramming boost-mpl boost-function

我正在尝试创建类似boost::function的东西,但内置一个额外的“智能句柄”,它控制了函数需要正确执行的资源的生命周期。

如果我不需要函数签名是完全通用的,那么这很容易。我只想创建一个带有正确签名的仿函数,并保留boost::function和句柄作为成员:

class library_function
{
public:
    library_function(
        library_handle handle,
        boost::function<result_type(arg1_type, arg2_type)> function)
        :
    m_handle(handle),
    m_function(function)
    {}

    typename result_type operator()(arg1_type arg1, arg2_type arg2)
    {
        return m_function(arg1, arg2);
    }

private:
    boost::function<result_type(arg1_type, arg2_type)> m_function;
    library_handle m_library;
};

但是,我确实需要通用签名。下面的代码尽可能接近但得到编译错误,大概是因为我无法解压缩这样的参数列表:

template<typename Signature>
class library_function
{
public:
    library_function(
        library_handle handle,
        boost::function<result_type(arg1_type, arg2_type)> function)
        :
    m_handle(handle),
    m_function(function)
    {}

    typename result_type operator()(arg1_type arg1, arg2_type arg2)
    {
        return m_function(arg1, arg2);
    }

    // I don't think I can declare the 'params' like this ...
    typename boost::function_types::result_type<Signature>::type operator()(
        boost::function_types::parameter_types<Signature> params)
    {
        return m_function(params); // ... nor unpack them like this
    }

private:
    boost::function<Signature> m_function;
    library_handle m_library;
};

我认为params是一个MPL列表,所以我实际完成的是使用单个MPL-list参数声明一个调用操作符。我的MPL-foo不好。有没有办法将列表转换为多个函数参数的合法定义?

或者,是否有办法让boost::function接受这个单个MPL列表并将其解压缩到可能的多参数可调用状态?


对于那些感兴趣的人,“智能句柄”是一个Windows DLL,其中一个函数已通过名称(GetProcAddress)加载。当DLL被销毁时,句柄将释放DLL,但是当任何人仍然想要调用该函数时,它不会发生。因此将其嵌入到callable中。

2 个答案:

答案 0 :(得分:2)

对于普通函数,您可以将library_function定义为:

template<typename Signature>
class library_function
{
public:
    library_function(
        library_handle lib, Signature* library_function)
        :
    m_library(lib),
    m_function(library_function)
   {}

    operator Signature*() const { return m_function; }

private:
   library_handle m_library;
   Signature* m_function;
};

技巧是operator Signature*()转换运算符, 当你使用library_function调用时,它允许编译器回退到普通函数m_function

你可以添加一个辅助函数,如:

template<typename Signature>
boost::function<Signature> make_library_function(library_handle library, Signature* f)
{
    return library_function<Signature>(library, f);
}

由于boost :: function会管理library_function的副本,因此也会管理'智能句柄'。

答案 1 :(得分:1)

希望我能回答如何在不回答如何编写通用函数类型的情况下完成用例。您可以使用boost :: function和boost :: bind以及boost :: shared_ptr来完成您所需要的操作,这比编写您尝试的代码更容易。除非我错过了一个用例,否则没有太多理由编写专门的library_function类型。

以下是使用int指针作为资源执行所要求的一些代码,但您可以将此模式应用于所需的任何类型。这是C ++ 11代码,但只有我使用它的std :: bind,std :: function和std :: shared_ptr。您可以直接用boost类型替换那些C ++ 11 std功能,它应该可以工作。 (我使用了C ++ 11,所以你可以看到它在这里运行:http://ideone.com/yIsg1E

#include <iostream>
#include <memory>
#include <functional>

using namespace std;
// Called by the shared_ptr to 'delete' the pointer.
struct cleanup {
    void operator() (int *i) {
        std::cout << "Cleaning up resource: " << i << "(" << *i << ")"
                  << std::endl; 
        // Do the work for releasing resource
        delete i;
    }
};

void use_temporary_resource(std::shared_ptr<int> resource,
                            const int &proof, const int &multiple) {
    int &i = *resource;
    std::cout << "Proof " << proof << ", using the resource: " << i << std::endl;
    std::cout << i << "*" << multiple << "=" << i*multiple << std::endl;
}

int main() {
    // Notice that an instance of cleanup() is passed to the shared_pointer construction
    std::shared_ptr<int> tempResource1(new int(1), cleanup());
    std::cout << "Resource 1 created: " 
              << tempResource1.get() << "(" << *tempResource1 << ")" << std::endl;

    // Resource is held onto with bind
    std::function<void(int i)> proof1 = std::bind(use_temporary_resource,
                                                  tempResource1,
                                                  1,
                                                  std::placeholders::_1);
    // If it wasn't this would close the resource
    tempResource1.reset();
    proof1(1);
    proof1(2); 
    {
    std::shared_ptr<int> tempResource2(new int(2), cleanup());
    // Resource is held onto with bind
    std::function<void(int i)> proof2 = std::bind(use_temporary_resource,
                                                    tempResource2,
                                                    2,
                                                    std::placeholders::_1);
    // If it wasn't this would close the resource
    tempResource2.reset();
    proof2(10);
    proof2(20);
    }  // Once scope goes away from proof2, the resource is released.
    std::cout << "Resource 2 should have been released after exiting scope containing proof2 (here)."
              << std::endl;

    std::shared_ptr<int> tempResource3(new int(3), cleanup());
        std::cout << "Resource 3 created: " 
                  << tempResource3.get() << "(" << *tempResource3 << ")" << std::endl;
    // Show that normally a resource will disapear if not placed in bind
    tempResource3.reset();
    std::cout << "Exiting..."  << std::endl; 
    return 0;
}

这是显示资源管理绑定到std :: function范围的输出:

Resource 1 created: 0x94b7008(1)
Proof 1, using the resource: 1
1*1=1
Proof 1, using the resource: 1
1*2=2
Proof 2, using the resource: 2
2*10=20
Proof 2, using the resource: 2
2*20=40
Cleaning up resource: 0x94b7048(2)
Resource 2 should have been released after exiting scope containing proof2 (here).
Resource 3 created: 0x94b7048(3)
Cleaning up resource: 0x94b7048(3)
Exiting...
Cleaning up resource: 0x94b7008(1)

总之,您只需为要扩展的资源创建一个带有自定义删除器的shared_pointer。然后将shared_pointer绑定到需要使用bind的函数。您实际上不必在函数中使用该参数,只需通过值传递shared_pointer就足够了。之后,只要绑定函数在范围内,即使您创建的shared_ptr最初放置范围,资源本身也将在范围内。

我希望这有帮助!