Boost.Python 和 Boost.Function

2023-12-19

我想包一个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/ http://bfroehle.com/2011/07/18/boost-python-and-boost-function-ii/

我在这里做错了什么?我需要做什么才能获得此功能所需的界面?

提前谢谢了。


Boost.Python 只接受函数指针和成员函数指针。所以我们需要做的就是将我们的可调用对象转换为函数指针。这里的关键想法是

  1. 没有捕获的 lambda 可以转换为函数指针(通过sorcery https://stackoverflow.com/q/18889028/2069064)
  2. 函数指针的解释方式与 Python 中的成员函数相同:第一个参数是self

所以在你的例子中,我们需要做的是生成这个 lambda:

+[](gui_button_t* self) {
    self->on_pressed();
}

您已经可以在 Boost.Python 中按原样使用它,因为这是一个完全正常的函数指针。但是,我们想要一个适用于以下情况的解决方案any可调用的成员。为什么just支持boost::function什么时候你可以支持任何事情?

我们将从@科伦坡的closure_traits https://stackoverflow.com/a/28213747/2069064,但还添加了一种提取参数列表的方法;

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 不能捕获,所以我们have将可调用对象作为模板参数:

template <typename CLS, typename F, F CLS::*callable>
class wrap { ... };

我将使用 C++14auto返回类型推导以节省一些打字。我们做的是顶级的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 http://coliru.stacked-crooked.com/a/939cb57f70c8e42b演示指向成员的函数指针std::function和一名会员structoperator().


上述内容使您能够expose一个可调用的。如果您还想能够进行作业,即:

button = GuiButton()
button.on_pressed = callback_function
button.on_pressed()

我们需要做点别的事情。你不能暴露operator=在Python中以一种有意义的方式,所以为了支持上述功能,你必须重写__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);

这可行,但您必须为要设置的每个函子添加更多案例。如果每个类只有一个类似函子的对象,可能没什么大不了的,甚至可以编写一个高阶函数来生成特定的setattr类似于给定属性名称的函数。但如果你有多个,它会逐渐变得比简单的更糟糕set_on_pressed解决方案。


如果 C++14 不可用,我们必须显式指定返回类型make_pointer。我们需要一些方便的类型特征。concat:

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 演示 http://coliru.stacked-crooked.com/a/d92e2289d6acd922.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boost.Python 和 Boost.Function 的相关文章

随机推荐

  • 不完全是。使用 Cocoa 处理游戏中键盘输入的正确方法是什么?

    假设您正在为 Mac OS X 创建游戏 事实上 假设您正在创建 Quake 只是现在是 2011 年 您更愿意只使用现代的 未弃用的框架 您希望您的游戏在用户按下 或释放 某个键时收到通知 any key 在键盘上 这包括修饰键 例如 S
  • 如何将 Git 中的更改合并到我移动的文件中?

    我移动了一些目录 当我合并时 有许多冲突的文件 因为其他开发人员已经提交了他们的更改 两者都有合并工具和git mergetool说文件被本地或远程删除 参见图片 如何合并这些更改 文件历史记录和重命名检测 你永远不需要担心 Git 中的
  • xcodebuild 构建操作来分发带有剥离 dSYM 的动态框架

    我曾经使用以下方法构建内部开发的动态框架 xcodebuild workspace
  • Jest 无法加载 Primeng css 文件

    我正在使用 Jest 版本 26 6 3 来测试 Angular 组件 对使用的任何组件进行单元测试Primeng的复选框组件 https www primefaces org primeng showcase checkbox在compi
  • 操作系统回调如何工作

    跟进问题 这个问题 https stackoverflow com questions 1223040 c timers in unix 如链接问题中所述 我们有一个 API 它使用事件外观来轮询 select 来处理用户定义的回调 我有一
  • 在 BigQuery 中获取不超过一小时的数据

    尝试使用以下语句 SELECT FROM data example WHERE TIMESTAMP timeCollected lt DATE ADD USEC TO TIMESTAMP NOW 60 MINUTE 从我的 bigquery
  • 使用 Storyboard Segues 关闭 Popover 视图

    我一直在谷歌搜索并在整个堆栈交换中搜索正确的答案 但我似乎找不到它 我所拥有的是通过弹出框 Segue 呈现的弹出框视图 当在弹出框视图内单击按钮时 我希望它被关闭并显示 UIAlert 这是迄今为止我从其他答案中收集的代码 但不起作用 v
  • 创建任务时无用的 Amazon ECS 错误消息

    使用ECS代理容器 https github com aws amazon ecs agent在 Ubuntu 实例上 我可以向我的集群注册代理 我还在该集群和任务定义中创建了一项服务 当我尝试将任务添加到集群时 我收到无用的错误消息 Ru
  • 在哪里初始化我的应用程序的基类 Swift

    我有一个名为Home这是我的应用程序的父类 现在 我想在某个地方初始化这个类 以便我可以从任何我想要的地方访问类中的所有内容 该应用程序的起点是RootViewController 我应该在起点初始化应用程序吗 如果是 我应该如何做才能从应
  • PyCharm:找不到 Anaconda 安装

    在使用 PyCharm 之前 我在 Windows 10 上将 Anaconda 安装在 C ProgramData Anaconda3 中 现在 当我尝试使用 conda env 时 PyCharm 显示 未找到 Anaconda 安装
  • 如何访问或翻译矢量绘图中的文本

    我有一个习惯view它显示一个drawable并检测触摸 它有多个drawable图层 其中一些包含文本 例如 我已将每个 SVG 矢量图形转换为vector drawable并希望能够将文本设置为资源 以便我可以替换不同的翻译 上面 He
  • 创建自托管 Web API 还是使用 Asp.Net Core Web API?

    我有两个选择 如果我使用自行托管 Web ApiOWIN Katana我可以获得性能提升 但如上所述here https forums asp net t 1908235 aspx Self hosting or IIS hosted 我必
  • Rails 按创建日期对记录进行分组

    所以我有一个模型 我想检索记录并按created at字段的日期对它们进行分组 但created at是一个日期时间字段 我只对日期部分感兴趣 所以我正在寻找类似二维数组的东西 第一层是一个以日期字符串为键的哈希 第二层是带有记录的数组 我
  • 我如何子类化 threading.Event?

    在 Python 2 7 5 中 from threading import Event class State Event def init self name super Event self init self name name d
  • Asp.Net MVC 4 中的自定义 URL 路由

    我怎样才能像这个网址一样 http www domain com friend content title http www domain com friendly content title 在 Asp Net MVC 4 中 注意 该参
  • 如何设置GAE环境特定的环境变量?

    我在 Go 中有一个 GAE 应用程序 使用martini https github com go martini martini 我需要能够设置MARTINI ENV环境变量告诉 martini 它应该使用生产设置进行初始化 根据Pyth
  • 仅包含 ASCII 字符的 UNICODE 字符串是否始终等于 ASCII 字符串?

    我注意到以下几点 gt gt gt u abc abc True gt gt gt abc u abc True 这总是正确的还是可能取决于系统区域设置 在 python 3 中字符串似乎是 unicode 例如这个问题 https sta
  • 如何删除 openlayer 中的特定标记

    这是来自 capdragon 更新的新代码 但现在它不会创建标记 我现在不知道如何解决这个问题 我认为代码片段是解决我之前识别标记问题的一种非常聪明的方法 而且它可能是我现在缺少的一个非常小的东西 任何再次帮助将不胜感激
  • 如何创建守护线程?

    java程序员可以手动创建守护线程吗 如何 java lang Thread setDaemon 布尔值 http java sun com javase 6 docs api java lang Thread html setDaemon
  • Boost.Python 和 Boost.Function

    我想包一个boost function类成员 以便可以按以下方式使用它 using namespace boost using namespace boost python struct gui button t function