Rip*_*lka 9 c++ python boost function boost-python
我目前正在使用Boost.Python,并希望得到一些帮助来解决一个棘手的问题.
上下文
当C++方法/函数暴露给Python时,它需要释放GIL(全局解释器锁)以让其他线程使用解释器.这样,当python代码调用C++函数时,解释器可以被其他线程使用.现在,每个C++函数看起来像这样:
// module.cpp
int myfunction(std::string question)
{
    ReleaseGIL unlockGIL;
    return 42;
}
为了传递它来提升python,我做:
// python_exposure.cpp
BOOST_PYTHON_MODULE(PythonModule)
{
    def("myfunction", &myfunction);
}
问题
这个方案工作正常,但它暗示这module.cpp取决于Boost.Python没有充分理由.理想情况下,只python_exposure.cpp应该依赖Boost.Python.
解?
我的想法是用Boost.Function这样包装函数调用:
// python_exposure.cpp
BOOST_PYTHON_MODULE(PythonModule)
{
    def("myfunction", wrap(&myfunction));
}
这里wrap将负责在通话期间解锁GIL myfunction.这种方法的问题是wrap需要具有相同的签名,myfunction这几乎意味着重新实现Boost.Function...
如果有人对此问题有任何建议,我将非常感激.
Tan*_*ury 13
没有正式支持将仿函数公开为方法.支持的方法是公开委托给成员函数的非成员函数.但是,这可能会导致大量的样板代码.
据我所知,Boost.Python的实现没有明确排除仿函数,因为它允许将实例python::object暴露为方法.但是,Boost.Python确实对作为方法公开的对象类型提出了一些要求:
o可以调用实例o(a1, a2, a3).boost::python::detail::get_signature()函数来获取此元数据.元数据在内部用于设置正确的调用,以及从Python到C++的调度.后一个要求是复杂的地方.由于某些原因我不能立即清楚,Boost.Python get_signature()通过qualified-id 调用,防止参数依赖查找.因此,get_signature()必须在调用模板的定义上下文之前声明所有候选项.例如,对于唯一的重载get_signature()被认为是那些来调用它的模板,如定义之前宣布class_,def()和make_function().为了解释这种行为,在Boost.Python中启用仿函数时,必须get_signature()在包含Boost.Python之前提供重载,或者显式提供表示签名的元序列make_function().
让我们通过一些启用仿函数支持的示例,以及提供支持守卫的仿函数.我选择不使用C++ 11功能.因此,可以使用可变参数模板减少一些样板代码.此外,所有示例都将使用提供两个非成员函数的相同模型和一个spam具有两个成员函数的类:
/// @brief Mockup class with member functions.
class spam
{
public:
  void action()
  {
    std::cout << "spam::action()"  << std::endl;
  }
  int times_two(int x)
  {
    std::cout << "spam::times_two()" << std::endl;
    return 2 * x;
  }
};
// Mockup non-member functions.
void action()
{
  std::cout << "action()"  << std::endl;
}
int times_two(int x)
{
  std::cout << "times_two()" << std::endl;
  return 2 * x;
}
boost::function当使用优选的句法对来自Boost.Function,分解成签名元数据符合的Boost.Python要求是可以做到Boost.FunctionTypes.这是一个完整的示例,使boost::function函子可以作为Boost.Python方法公开:
#include <iostream>
#include <boost/function.hpp>
#include <boost/function_types/components.hpp>
namespace boost  {
namespace python {
namespace detail {
// get_signature overloads must be declared before including
// boost/python.hpp.  The declaration must be visible at the
// point of definition of various Boost.Python templates during
// the first phase of two phase lookup.  Boost.Python invokes the
// get_signature function via qualified-id, thus ADL is disabled.
/// @brief Get the signature of a boost::function.
template <typename Signature>
inline typename boost::function_types::components<Signature>::type
get_signature(boost::function<Signature>&, void* = 0)
{
  return typename boost::function_types::components<Signature>::type();
}
} // namespace detail
} // namespace python
} // namespace boost
#include <boost/python.hpp>
/// @brief Mockup class with member functions.
class spam
{
public:
  void action()
  {
    std::cout << "spam::action()"  << std::endl;
  }
  int times_two(int x)
  {
    std::cout << "spam::times_two()" << std::endl;
    return 2 * x;
  }
};
// Mockup non-member functions.
void action()
{
  std::cout << "action()"  << std::endl;
}
int times_two(int x)
{
  std::cout << "times_two()" << std::endl;
  return 2 * x;
}
BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Expose class and member-function.
  python::class_<spam>("Spam")
    .def("action",  &spam::action)
    .def("times_two", boost::function<int(spam&, int)>(
        &spam::times_two))
    ;
  // Expose non-member function.
  python::def("action",  &action);
  python::def("times_two", boost::function<int()>(
      boost::bind(×_two, 21)));
}
它的用法:
>>> import example
>>> spam = example.Spam()
>>> spam.action()
spam::action()
>>> spam.times_two(5)
spam::times_two()
10
>>> example.action()
action()
>>> example.times_two()
times_two()
42
当提供将调用成员函数的仿函数时,提供的签名需要是非成员函数等效函数.在这种情况下,int(spam::*)(int)成为int(spam&, int).
// ...
  .def("times_two", boost::function<int(spam&, int)>(
        &spam::times_two))
  ;
此外,参数可以绑定到仿函数boost::bind.例如,调用example.times_two()不必提供参数,因为21已经绑定到仿函数.
python::def("times_two", boost::function<int()>(
    boost::bind(×_two, 21)));
扩展上面的示例,可以启用自定义函子类型与Boost.Python一起使用.让我们创建一个名为funide的函数,guarded_function它将使用RAII,只在RAII对象的生命周期内调用包装函数.
/// @brief Functor that will invoke a function while holding a guard.
///        Upon returning from the function, the guard is released.
template <typename Signature,
          typename Guard>
class guarded_function
{
public:
  typedef typename boost::function_types::result_type<Signature>::type
      result_type;
  template <typename Fn>
  guarded_function(Fn fn)
    : fn_(fn)
  {}
  result_type operator()()
  {
    Guard g;
    return fn_();
  }
  // ... overloads for operator()
private:
  boost::function<Signature> fn_;
};
它guarded_function提供了与Python with语句类似的语义.因此,为了与Boost.Python API名称选择保持一致,with()C++函数将提供一种创建仿函数的方法.
/// @brief Create a callable object with guards.
template <typename Guard,
          typename Fn>
boost::python::object
with(Fn fn)
{
   return boost::python::make_function(
     guarded_function<Guard, Fn>(fn), ...);
}
这允许暴露函数,这些函数将以非侵入方式与警卫一起运行:
class no_gil; // Guard
// ...
  .def("times_two", with<no_gil>(&spam::times_two))
  ;
此外,该with()函数提供了推断函数签名的功能,允许将元数据签名显式提供给Boost.Python而不必重载boost::python::detail::get_signature().
以下是使用两种RAII类型的完整示例:
no_gil:在构造函数中释放GIL,并在析构函数中重新获取GIL.echo_guard:在构造函数和析构函数中打印.#include <iostream>
#include <boost/function.hpp>
#include <boost/function_types/components.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/python.hpp>
#include <boost/tuple/tuple.hpp>
namespace detail {
/// @brief Functor that will invoke a function while holding a guard.
///        Upon returning from the function, the guard is released.
template <typename Signature,
          typename Guard>
class guarded_function
{
public:
  typedef typename boost::function_types::result_type<Signature>::type
      result_type;
  template <typename Fn>
  guarded_function(Fn fn)
    : fn_(fn)
  {}
  result_type operator()()
  {
    Guard g;
    return fn_();
  }
  template <typename A1>
  result_type operator()(A1 a1)
  {
    Guard g;
    return fn_(a1);
  }
  template <typename A1, typename A2>
  result_type operator()(A1 a1, A2 a2)
  {
    Guard g;
    return fn_(a1, a2);
  }
private:
  boost::function<Signature> fn_;
};
/// @brief Provides signature type.
template <typename Signature>
struct mpl_signature
{
  typedef typename boost::function_types::components<Signature>::type type;
};
// Support boost::function.
template <typename Signature>
struct mpl_signature<boost::function<Signature> >:
  public mpl_signature<Signature>
{};
/// @brief Create a callable object with guards.
template <typename Guard,
          typename Fn,
          typename Policy>
boost::python::object with_aux(Fn fn, const Policy& policy)
{
  // Obtain the components of the Fn.  This will decompose non-member
  // and member functions into an mpl sequence.
  //   R (*)(A1)    => R, A1
  //   R (C::*)(A1) => R, C*, A1
  typedef typename mpl_signature<Fn>::type mpl_signature_type;
  // Synthesize the components into a function type.  This process
  // causes member functions to require the instance argument.
  // This is necessary because member functions will be explicitly
  // provided the 'self' argument.
  //   R, A1     => R (*)(A1)
  //   R, C*, A1 => R (*)(C*, A1)
  typedef typename boost::function_types::function_type<
      mpl_signature_type>::type signature_type;
  // Create a callable boost::python::object that delegates to the
  // guarded_function.
  return boost::python::make_function(
    guarded_function<signature_type, Guard>(fn),
    policy, mpl_signature_type());
}
} // namespace detail
/// @brief Create a callable object with guards.
template <typename Guard,
          typename Fn,
          typename Policy>
boost::python::object with(const Fn& fn, const Policy& policy)
{
  return detail::with_aux<Guard>(fn, policy);
}
/// @brief Create a callable object with guards.
template <typename Guard,
          typename Fn>
boost::python::object with(const Fn& fn)
{
  return with<Guard>(fn, boost::python::default_call_policies());
}
/// @brief Mockup class with member functions.
class spam
{
public:
  void action()
  {
    std::cout << "spam::action()"  << std::endl;
  }
  int times_two(int x)
  {
    std::cout << "spam::times_two()" << std::endl;
    return 2 * x;
  }
};
// Mockup non-member functions.
void action()
{
  std::cout << "action()"  << std::endl;
}
int times_two(int x)
{
  std::cout << "times_two()" << std::endl;
  return 2 * x;
}
/// @brief Guard that will unlock the GIL upon construction, and
///        reacquire it upon destruction.
struct no_gil
{
public:
  no_gil()  { state_ = PyEval_SaveThread(); 
              std::cout << "no_gil()" << std::endl; }
  ~no_gil() { std::cout << "~no_gil()" << std::endl;
              PyEval_RestoreThread(state_); }
private:
  PyThreadState* state_;
};
/// @brief Guard that prints to std::cout.
struct echo_guard 
{
  echo_guard()  { std::cout << "echo_guard()" << std::endl;  }
  ~echo_guard() { std::cout << "~echo_guard()" << std::endl; }
};
BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Expose class and member-function.
  python::class_<spam>("Spam")
    .def("action", &spam::action)
    .def("times_two", with<no_gil>(&spam::times_two))
    ;
  // Expose non-member function.
  python::def("action", &action);
  python::def("times_two", with<boost::tuple<no_gil, echo_guard> >(
      ×_two));
}
它的用法:
>>> import example
>>> spam = example.Spam()
>>> spam.action()
spam::action()
>>> spam.times_two(5)
no_gil()
spam::times_two()
~no_gil()
10
>>> example.action()
action()
>>> example.times_two(21)
no_gil()
echo_guard()
times_two()
~echo_guard()
~no_gil()
42
请注意如何使用容器类型提供多个防护,例如boost::tuple:
  python::def("times_two", with<boost::tuple<no_gil, echo_guard> >(
      ×_two));
在Python中调用时,example.times_two(21)生成以下输出:
no_gil()
echo_guard()
times_two()
~echo_guard()
~no_gil()
42
| 归档时间: | 
 | 
| 查看次数: | 3114 次 | 
| 最近记录: |