的boost :: Python类与函数模板:如何从外界补充实例? [英] Boost::Python class with function templates: How to add instances from the outside?

查看:181
本文介绍了的boost :: Python类与函数模板:如何从外界补充实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘要

有没有一种方法(在C ++中,不是在Python)来添加的功能模板的其他实例从外向内的boost :: Python中的类(通过注射,重新开放的定义,注册所需的实例等)<? / p>

背景

由于包含成员是函数模板类(不是类模板),我要生成使用boost :: Python的Python绑定。

不过,正如我写一个库,我不事先知道哪个模板参数的成员函数将被调用。这意味着,我不能在升压:: Python类的定义。

一一列举

示例

让我们说我们有一个类 theClass描述具有函数模板(带过载),以及两个测试类 SomeClass的 OtherClass 是这样的:

类定义

 的#include&LT;&iostream的GT;
#包括LT&;串GT;类SomeClass的
{
上市:
    标准::字符串名称()
    {
        返回SomeClass的;
    }
};类OtherClass
{
上市:
    标准::字符串名称()
    {
        返回OtherClass
    }
};类theClass描述
{
上市:    模板&LT;类T&GT;
    无效美孚(T&安培; ARG)
    {
        性病::法院LT&;&LT; 名为foo(&LT;&LT; arg.Name()&LT;&LT;)&LT;&LT;的std :: ENDL;
    }    模板&LT;类T&GT;
    酒吧无效(T&安培;阿根廷,标准::字符串参数)
    {
        性病::法院LT&;&LT; 被叫栏(&所述;&下; arg.Name()&所述;&下;,&所述;&所述; PARAM&所述;&下;)&所述;&下;的std :: ENDL;
    }    模板&LT;类T&GT;
    酒吧无效(T&安培;阿根廷,INT参数)
    {
        性病::法院LT&;&LT; 被叫栏(&所述;&下; arg.Name()&所述;&下;,&所述;&所述; PARAM&所述;&下;)&所述;&下;的std :: ENDL;
    }};

然后我用这个code出口上述所有到Python:

升压的Python导出

 的#include&LT;升压/ python.hpp&GT;#定义GENERATE_THE_CLASS_METHODS(类名)\\
    .DEF(\\
        富,\\
        (无效(theClass描述:: *)(类名和放大器;))(安培; theClass描述::美孚),\\
        (::提高::蟒ARG(ARG))\\
    )\\
    .DEF(\\
        酒吧,\\
        (无效(theClass描述:: *)(类名和放大器;,的std ::字符串))(安培; theClass描述::吧),\\
        (::提高::蟒ARG(ARG),提振::蟒蛇:: ARG(参数))\\
    )\\
    .DEF(\\
        酒吧,\\
        (无效(theClass描述:: *)(类名和放大器;,INT))(安培; theClass描述::吧),\\
        (::提高::蟒ARG(ARG),提振::蟒蛇:: ARG(参数))\\
    )BOOST_PYTHON_MODULE(my_module)
{
    提高::蟒蛇:: class_&LT; theClass描述&GT; ( 班上 )
        GENERATE_THE_CLASS_METHODS(SomeClass的)
        GENERATE_THE_CLASS_METHODS(OtherClass)        //这是最有趣的部分:函数的所有实例
        //模板必须插入此处。这怎么能避免
        //使新的类也可以使用?    ;    提高::蟒蛇:: class_&LT; SomeClass的&GT; (SomeClass的);
    提高::蟒蛇:: class_&LT; OtherClass&GT; (OtherClass);
}

(侧面的问题:我是否为了避免重复code为维护原因在这里使用宏有一个更美丽,C ++ - 实现这一目标的方式ISH)

的Python测试脚本

上面的code编译使用锵用C ++ 11,升压1.57.0和Python 2.7.6。它的工作原理与此测试脚本:

 #!的/ usr / bin中/蟒蛇从my_module进口*S = SomeClass的()
O = OtherClass()
T = theClass描述()t.Foo(多个)
t.Foo(O)t.Bar(S,42)
t.Bar(0,42)
t.Bar(S,的Hello World)
t.Bar(0,Hello World的)

这屈服outout:

 叫做foo(SomeClass的)
叫做foo(OtherClass)
所谓的酒吧(SomeClass的,42)
所谓的酒吧(OtherClass,42)
所谓的酒吧(SomeClass的,的Hello World)
所谓的酒吧(OtherClass,世界您好)

问题

在这个例子中,为富()和bar()函数模板实例化通过了boost :: Python类定义(见源$ C ​​$ C评论)内部创建的。这意味着,无需修改code的该位的库的用户不能添加一个新的实例。

因此​​,我所寻找的是一个方法,要么


  • 注入来自Boost Python类定义的::之外的实例

  • 不知何故重新打开定义

  • 了boost :: Python类定义称为以前在什么地方注册所需的实例

在最后,库的用户应该能够做这样的事:

 类AnotherClass
{
上市:
    标准::字符串名称()
    {
        返回AnotherClass
    }
};add_to_the_class(AnotherClass);
// 要么
add_to_class_definition&所述; AnotherClass&GT(theClass描述);
//或任何作品...

这是某种程度上可能吗?是否有其他方法来实现类似的东西?


解决方案

过了一会儿,我发现了一个解决方案,并推断这可能是其他有趣的为好。

解决方案

它实际上是pretty方便:采用的boost ::蟒蛇::类_ 定义的回报(当然)类型的类实例提高::蟒蛇:: class_&LT; theClass描述&GT; 。这可以被储存,所以我们可以在以后添加成员定义它:

 静态自动the_class_ =提振::蟒蛇:: class_&LT; theClass描述&GT; ( 班上 )
    //添加一些.DEF(...)等在这里,如果需要的话
;模板&LT;类T&GT;
无效add_to_the_class()
{
    班上_
        .DEF(
            富,
            (无效(theClass描述:: *)(T&安培;))(安培; theClass描述::美孚),
            (::提高::蟒ARG(ARG))
        )
        .DEF(
            酒吧,
            (无效(theClass描述:: *)(T&放;,的std ::字符串))(安培; theClass描述::吧),
            (::提高::蟒ARG(ARG),提振::蟒蛇:: ARG(参数))
        )
        .DEF(
            酒吧,
            (无效(theClass描述:: *)(T&安培;,INT))(安培; theClass描述::吧),
            (::提高::蟒ARG(ARG),提振::蟒蛇:: ARG(参数))
        )
    ;
}

现在,我们可以为许多额外的重载从外面,因为我们希望加入到这个定义:

  add_to_the_class&LT; AnotherClass&GT;();

这也摆脱了丑陋的宏。

一些额外的洞察力

这所有的作品,因为对Python的实际绑定在运行时创建的。在你的boost :: Python绑定code,你实际上只是定义一些类和函数 - 这之前调用的main()来启动你的C ++ code和Python的跨preTER之间的实际联系。

我想,这是没有得到很好的助推::蟒蛇的帮助文档。我花了一段时间来弄清楚这一点。一开始,我还以为那莫名其妙的boost ::蟒蛇(通过一些模板魔术)在编译时产生的绑定。如果这是真的,上述解决方案是行不通的。事后看来,这是现在所有我清楚。

此外,这种洞察力(绑定在运行时完成,根据您提供的定义)也给了我的想法写,收集导出到Python类的静态寄存器类别。这将有助于我的图书馆的做法更进一步,因为我不会有延长主 BOOST_PYTHON_MODULE 定义为每个新的类,而是简单的注册类的静态寄存器(可能再次使用一些宏...)。但是,这是未来的工作...问题解决了,现在!

Summary

Is there a way (in C++, not in Python) to add additional instantiations of function templates for a class in Boost::Python from the outside (by injection, reopening the definition, registering the needed instantiations, etc)?

Background

Given a class (not a class template) containing members that are function templates, I want to generate Python bindings using Boost::Python.

However, as I am writing a library, I do not know beforehand which template parameters the member functions will be called with. That means, I cannot list them in the Boost::Python class definition.

Example

Let's say we have a class TheClass that has function templates (with overloads), and two test classes SomeClass and OtherClass like this:

Class Definitions

#include <iostream>
#include <string>

class SomeClass
{
public:
    std::string Name()
    {
        return "SomeClass";
    }
};

class OtherClass
{
public:
    std::string Name()
    {
        return "OtherClass";
    }
};

class TheClass
{
public:

    template <class T>
    void Foo   (T& arg)
    {
        std::cout << "Called Foo(" << arg.Name() << ")" << std::endl;
    }

    template <class T>
    void Bar   (T& arg, std::string param)
    {
        std::cout << "Called Bar(" << arg.Name() << ", " << param << ")" << std::endl;
    }

    template <class T>
    void Bar   (T& arg, int param)
    {
        std::cout << "Called Bar(" << arg.Name() << ", " << param << ")" << std::endl;
    }

};

Then I use this code for exporting all of the above to Python:

Boost Python Export

#include <boost/python.hpp>

#define GENERATE_THE_CLASS_METHODS(classname)                                 \
    .def(                                                                     \
        "Foo",                                                                \
        ( void ( TheClass::* )( classname& ))( &TheClass::Foo ),              \
        ( boost::python::arg("arg") )                                         \
    )                                                                         \
    .def(                                                                     \
        "Bar",                                                                \
        ( void ( TheClass::* )( classname&, std::string ))( &TheClass::Bar ), \
        ( boost::python::arg("arg"), boost::python::arg("param") )            \
    )                                                                         \
    .def(                                                                     \
        "Bar",                                                                \
        ( void ( TheClass::* )( classname&, int ))( &TheClass::Bar ),         \
        ( boost::python::arg("arg"), boost::python::arg("param") )            \
    )

BOOST_PYTHON_MODULE(my_module)
{
    boost::python::class_< TheClass > ( "TheClass" )
        GENERATE_THE_CLASS_METHODS(SomeClass)
        GENERATE_THE_CLASS_METHODS(OtherClass)

        // This is the interesting part: all instantiations of the function
        // templates have to be inserted here. How can this be avoided
        // so that new classes can also be used?

    ;

    boost::python::class_< SomeClass > ( "SomeClass" );
    boost::python::class_< OtherClass > ( "OtherClass" );
}

(Side question: I use a macro here in order to avoid duplicated code for maintenance reasons. Is there a more beautiful, C++-ish way of achieving this?)

Python Test Script

The code above compiles using Clang with C++11, Boost 1.57.0 and Python 2.7.6. It works with this test script:

#!/usr/bin/python

from my_module import *

s = SomeClass()
o = OtherClass()
t = TheClass()

t.Foo(s)
t.Foo(o)

t.Bar(s, 42)
t.Bar(o, 42)
t.Bar(s, "Hello World")
t.Bar(o, "Hello World")

Yielding this outout:

Called Foo(SomeClass)
Called Foo(OtherClass)
Called Bar(SomeClass, 42)
Called Bar(OtherClass, 42)
Called Bar(SomeClass, Hello World)
Called Bar(OtherClass, Hello World)

Question

In the example, the instantiations of the function templates for Foo() and Bar() are created inside the Boost::Python class definition (see the comment in the source code). This means, a user of the library cannot add a new instantiation without modifying this bit of code.

Thus, what I am looking for is a way to either

  • "inject" those instantiations from the outside of the Boost::Python class definition
  • re-open the definition somehow
  • register the needed instantiations somewhere before the Boost::Python class definition is called

In the end, a user of the library should be able to do something like this:

class AnotherClass
{
public:
    std::string Name()
    {
        return "AnotherClass";
    }
};

add_to_the_class(AnotherClass);
// or
add_to_class_definition<AnotherClass>("TheClass");
// or whatever works...

Is this somehow possible? Are there other ways to achieve something similar?

解决方案

After a while, I found a solution and figured this might be interesting for others as well.

Solution

It is actually pretty easy: The boost::python::class_ definition returns (of course) a class instance of type boost::python::class_< TheClass >. This can be stored, so we can add member definitions to it later:

static auto the_class_ = boost::python::class_< TheClass > ( "TheClass" )
    // add some .def(...) and so on here, if needed
;

template <class T>
void add_to_the_class()
{
    the_class_
        .def(
            "Foo",
            ( void ( TheClass::* )( T& ))( &TheClass::Foo ),
            ( boost::python::arg("arg") )
        )
        .def(
            "Bar",
            ( void ( TheClass::* )( T&, std::string ))( &TheClass::Bar ),
            ( boost::python::arg("arg"), boost::python::arg("param") )
        )
        .def(
            "Bar",
            ( void ( TheClass::* )( T&, int ))( &TheClass::Bar ),
            ( boost::python::arg("arg"), boost::python::arg("param") )
        )
    ;
}

Now we can add as many extra overloads to this definition from the outside as we want:

add_to_the_class<AnotherClass>();

This also gets rid of the ugly macro.

Some additional insight

This all works because the actual binding to Python is created at runtime. In your boost::python binding code, you actually just define some classes and functions -- which are called before main() to initiate the actual connection between your C++ code and the Python interpreter.

I think, this is not well documented in the boost::python help. It took me a while to figure this out. In the beginning, I thought that boost::python somehow (via some template magic) produces the bindings during compile time. If this was true, the solution above would not work. In hindsight, this is all clear to me now.

Furthermore, this insight (binding is done at runtime, using the definitions you provide) also gave me the idea to write a static register class that collects classes to be exported to Python. This would help my library approach even further, as I would not have to extend the main BOOST_PYTHON_MODULE definition for each new class, but instead simply register the class to the static register (probably using some macro again...). But this is future work... Problem solved for now!

这篇关于的boost :: Python类与函数模板:如何从外界补充实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆