Boost :: Python类与功能模板:如何从外部添加实例? [英] Boost::Python class with function templates: How to add instances from the outside?

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

问题描述

摘要



有一种方法(在C ++中,不是在Python中)从外部为Boost :: Python中的类添加额外的函数模板实例背景

给定一个类(例如,通过注入,重新打开定义,注册所需的实例化等)。



<不是类模板)包含作为函数模板的成员,我想使用Boost :: Python生成Python绑定。



但是,因为我正在写一个库,我不知道事先调用哪些模板参数的成员函数。这意味着我不能在Boost :: Python类定义中列出它们。



示例



有一个类 TheClass 有函数模板(带重载)和两个测试类 SomeClass OtherClass 如下:



类定义



  #include< iostream> 
#include< string>

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

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

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;
}

};

然后我使用这个代码将上述所有内容导出到Python:



Boost Python汇出



  #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),\
python :: arg(arg),boost :: python :: arg(param))\
)\
.def(\
Bar \\
(void(TheClass :: *)(classname& int))(& TheClass :: Bar),\
(boost :: python :: arg :: python :: arg(param))\


BOOST_PYTHON_MODULE(my_module)
{
boost :: python :: class_&类> (TheClass)
GENERATE_THE_CLASS_METHODS(SomeClass)
GENERATE_THE_CLASS_METHODS(OtherClass)

//这是有趣的部分:函数的所有实例
//模板插入这里。如何避免这种情况
//这样新的类也可以使用?

;

boost :: python :: class_< SomeClass> (SomeClass);
boost :: python :: class_<其他类> (OtherClass);
}

(问题:我在这里使用宏,以避免重复的代码维护原因是否有更美观的C ++ - ish方式实现这一点?)



Python测试脚本



上面的代码使用Clang与C ++ 11,Boost 1.57.0和Python 2.7.6进行编译。它与此测试脚本配合使用:

 #!/ usr / bin / python 

从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)

产生此outout:



调用Foo(SomeClass)
调用Foo(OtherClass)
调用Bar(SomeClass,42)
调用Bar其他类,42)
调用的bar(SomeClass,Hello World)
调用的bar(OtherClass,Hello World)



问题



在示例中,Foo()和Bar()的函数模板的实例化在Boost :: Python类中创建定义(见源代码中的注释)。这意味着,库的用户不能添加一个新的实例化而不修改这一段代码。



因此,我要找的是一种方式




  • 从boost :: Python类定义的外部注入这些实例化

  • 重新打开




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

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

add_to_the_class(AnotherClass);
//或
add_to_class_definition< AnotherClass>(TheClass);
//或其他什么工作...

这是不是可能?有没有其他方法来实现类似的东西?

解决方案

过一段时间后,我发现了一个解决方案,



解决方案



其实很容易: boost :: python :: class _ 定义返回(当然)类型的类实例 boost :: python :: class_< TheClass> 。这可以存储,所以我们可以在以后添加成员定义:

  static auto the_class_ = boost :: python :: class_< ;类> (TheClass)
//添加一些.def(...)等等,如果需要
;

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 :: *) ; TheClass :: Bar),
(boost :: python :: arg(arg),boost :: python :: arg(param))

;
}

现在我们可以从外部添加额外的重载想要:

  add_to_the_class< AnotherClass>(); 

这也摆脱了丑陋的宏。



一些额外的洞察力



这一切都是有效的,因为实际绑定到Python是在运行时创建的。在boost :: python绑定代码中,你实际上只需定义一些类和函数 - 在main()之前调用,以启动C ++代码和Python解释器之间的实际连接。



我想,这没有很好地记录在boost :: python帮助。我花了一会儿想出这个。在开始,我认为boost :: python(通过一些模板魔术)在编译时产生绑定。如果这是真的,上面的解决方案不会工作。



此外,这个洞察(绑定是在运行时完成的,使用你提供的定义)也给了我写的想法一个静态寄存器类,它收集要导出到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天全站免登陆