- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我想包装一个 boost::function
类成员,以便可以按以下方式使用它:
using namespace boost;
using namespace boost::python;
struct gui_button_t
{
function<void()> on_pressed;
};
class_<gui_button_t>("GuiButton", init<>())
.def("on_pressed", &gui_button_t::on_pressed);
然后在 Python 中:
def callback_function():
print 'button has been pressed'
button = GuiButton()
button.on_pressed = callback_function
button.on_pressed() # function should be callable from C++ or Python
但是,尝试这样做会产生大量关于类模板参数等的错误。
我进行了一些搜索,但未能找到我一直在寻找的答案。以下文章有点接近,但没有直接触及主题。
http://bfroehle.com/2011/07/18/boost-python-and-boost-function-ii/
我在这里做错了什么?我需要做什么才能获得此功能所需的界面?
非常感谢。
最佳答案
Boost.Python 只接受指向函数的指针和指向成员函数的指针。所以我们需要做的是将我们的可调用对象转换为函数指针。这里的关键思想是
self
所以在你的情况下,我们需要做的是生成这个 lambda:
+[](gui_button_t* self) {
self->on_pressed();
}
您已经可以在 Boost.Python 中按原样使用它,因为它是一个完全正常的函数指针。但是,我们想要一个适用于任何 可调用成员的解决方案。当您可以支持任何东西时,为什么只是支持boost::function
?
我们将从 @Columbo's closure_traits
开始,但另外添加了一种提取参数列表的方法;
template <typename...> struct typelist { };
template <typename C, typename R, typename... Args> \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv> \
{ \
using arity = std::integral_constant<std::size_t, sizeof...(Args) >; \
using is_variadic = std::integral_constant<bool, is_var>; \
using is_const = std::is_const<int cv>; \
\
using result_type = R; \
\
template <std::size_t i> \
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \
\
using args = typelist<Args...>; \
};
然后我们将为任何可调用成员编写一个包装器。由于我们的 lambda 不能捕获,我们必须将可调用对象作为模板参数:
template <typename CLS, typename F, F CLS::*callable>
class wrap { ... };
我将使用 C++14 的 auto
返回类型推导来节省一些输入。我们制作了一个顶级 make_pointer()
静态成员函数,它只是转发给一个额外接受参数的辅助成员函数。完整的 wrap
看起来像:
template <typename CLS, typename F, F CLS::*callable>
class wrap {
public:
static auto make_pointer() {
return make_pointer_impl(typename closure_traits<F>::args{});
}
private:
template <typename... Args>
static auto make_pointer_impl(typelist<Args...> ) {
// here is our lambda that takes the CLS as the first argument
// and then the rest of the callable's arguments,
// and just calls it
return +[](CLS* self, Args... args) {
return (self->*callable)(args...);
};
}
};
我们可以用它来包装您的按钮:
void (*f)(gui_button_t*) = wrap<gui_button_t,
decltype(gui_button_t::on_pressed),
&gui_button_t::on_pressed
>::make_pointer();
这有点冗长和重复,所以让我们做一个宏吧(叹气):
#define WRAP_MEM(CLS, MEM) wrap<CLS, decltype(CLS::MEM), &CLS::MEM>::make_pointer()
所以我们得到:
void (*f)(gui_button_t*) = WRAP_MEM(gui_button_t, on_pressed);
f(some_button); // calls some_button->on_pressed()
因为这给了我们一个指向函数的指针,我们可以直接将它与普通的 Boost.Python API 一起使用:
class_<gui_button_t>("GuiButton", init<>())
.def("on_pressed", WRAP_MEM(gui_button_t, on_pressed));
Demo演示指向成员 std::function
和带有 operator()
的成员 struct
的函数指针。
以上内容让您能够公开一个可调用对象。如果您还想能够进行分配,即:
button = GuiButton()
button.on_pressed = callback_function
button.on_pressed()
我们还需要做点别的。您不能在 Python 中以有意义的方式公开 operator=
,因此要支持上述功能,您必须改写 __setattr__
。现在,如果您愿意:
button.set_on_pressed(callback_function)
我们可以扩展上面的 wrap
解决方案来添加一个 setter,其实现将按照上面的思路:
static auto set_callable() {
return make_setter_impl(
typelist<typename closure_traits<F>::result_type>{},
typename closure_traits<F>::args{});
}
template <typename R, typename... Args>
static auto make_setter_impl(typelist<R>, typelist<Args...> ) {
return +[](CLS* self, py::object cb) {
(self->*callable) = [cb](Args... args) {
return py::extract<R>(
cb(args...))();
};
};
}
// need a separate overload just for void
template <typename... Args>
static auto make_setter_impl(typelist<void>, typelist<Args...> ) {
return +[](CLS* self, py::object cb) {
(self->*callable) = [cb](Args... args) {
cb(args...);
};
};
}
#define SET_MEM(CLS, MEM) wrap<CLS, decltype(CLS::MEM), &CLS::MEM>::set_callable()
然后您可以通过以下方式公开:
.def("set_on_pressed", SET_MEM(button, on_pressed))
但是,如果您坚持支持直接赋值,那么您将需要另外暴露如下内容:
static void setattr(py::object obj, std::string attr, py::object val)
{
if (attr == "on_pressed") {
button& b = py::extract<button&>(obj);
SET_MEM(button, on_pressed)(&b, val);
}
else {
py::str attr_str(attr);
if (PyObject_GenericSetAttr(obj.ptr(), attr_str.ptr(), val.ptr()) {
py::throw_error_already_set();
}
}
}
.def("__setattr__", &button::setattr);
这可行,但您必须为每个要设置的仿函数添加更多的 case。如果每个类只有一个类似仿函数的对象,可能没什么大不了的,甚至可以编写一个高阶函数来为给定的属性名称生成一个特定的类似 setattr
的函数。但是如果你有多个,它会逐渐变得比简单的 set_on_pressed
解决方案更糟糕。
如果 C++14 不可用,我们将不得不明确指定 make_pointer
的返回类型。我们需要一些方便的类型特征。 连接
:
template <typename T1, typename T2>
struct concat;
template <typename T1, typename T2>
using concat_t = typename concat<T1, T2>::type;
template <typename... A1, typename... A2>
struct concat<typelist<A1...>, typelist<A2...>> {
using type = typelist<A1..., A2...>;
};
然后将返回类型和 typelist
转换为函数指针:
template <typename R, typename T>
struct make_fn_ptr;
template <typename R, typename... Args>
struct make_fn_ptr<R, typelist<Args...>> {
using type = R(*)(Args...);
};
template <typename R, typename T>
using make_fn_ptr_t = typename make_fn_ptr<R, T>::type;
然后在 wrap
中,我们可以将结果类型定义为:
using R = make_fn_ptr_t<
typename closure_traits<F>::result_type,
concat_t<
typelist<CLS*>,
typename closure_traits<F>::args
>
>;
并使用它代替 auto
。 C++11 Demo .
关于python - Boost.Python 和 Boost.Function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30553586/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!