Boost :: Класс Python с шаблонами функций: как добавить экземпляры извне?

Резюме

Есть ли способ (в C ++, а не в Python) добавлять дополнительные экземпляры шаблонов функций для класса в Boost :: Python извне (путем инъекции, повторного открытия определения, регистрации необходимых экземпляров и т. Д.)?

Задний план

Учитывая класс (а не шаблон класса), содержащий элементы, являющиеся функциональными шаблонами, я хочу сгенерировать привязки Python с помощью Boost :: Python.

Однако, поскольку я пишу библиотеку, я заранее не знаю, какие параметры шаблона будут вызывать с помощью функций-членов. Это означает, что я не могу перечислить их в определении класса Boost :: Python.

пример

Предположим, у нас есть класс TheClass , у которого есть функциональные шаблоны (с перегрузками) и два тестовых класса SomeClass и OtherClass :

Определения классов

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

Затем я использую этот код для экспорта всего вышеперечисленного в Python:

Повысить экспорт 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 ), \ ( 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" ); } 

(Боковой вопрос: я использую макрос здесь, чтобы избежать дублирования кода по причинам обслуживания. Есть ли более красивый, C ++-иш способ достижения этого?)

Тест скрипта Python

Вышеприведенный код компилируется с использованием Clang с C ++ 11, Boost 1.57.0 и Python 2.7.6. Он работает с этим тестовым скриптом:

 #!/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") 

Выход этого выхода:

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

Вопрос

В этом примере экземпляры шаблонов функций для Foo () и Bar () создаются внутри определения класса Boost :: Python (см. Комментарий в исходном коде). Это означает, что пользователь библиотеки не может добавить новый экземпляр без изменения этого бита кода.

Таким образом, то, что я ищу, – это способ либо

  • «вводить» эти экземпляры извне определения класса Boost :: Python
  • отменить определение как-то
  • зарегистрируйте необходимые экземпляры где-нибудь перед вызовом класса Boost :: Python

В конце концов, пользователь библиотеки должен иметь возможность сделать что-то вроде этого:

 class AnotherClass { public: std::string Name() { return "AnotherClass"; } }; add_to_the_class(AnotherClass); // or add_to_class_definition<AnotherClass>("TheClass"); // or whatever works... 

Это возможно? Есть ли другие способы добиться чего-то подобного?

One Solution collect form web for “Boost :: Класс Python с шаблонами функций: как добавить экземпляры извне?”

Через некоторое время я нашел решение и понял, что это может быть интересно и для других.

Решение

На самом деле это довольно просто: определение boost::python::class_ возвращает (конечно) экземпляр класса типа boost::python::class_< TheClass > . Это можно сохранить, поэтому мы можем добавить к нему определения элементов позже:

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

Теперь мы можем добавить столько дополнительных перегрузок в это определение извне, сколько хотим:

 add_to_the_class<AnotherClass>(); 

Это также избавляет от уродливого макроса.

Дополнительная информация

Все это работает, потому что фактическая привязка к Python создается во время выполнения. В коде boost: python binding вы фактически определяете некоторые классы и функции, которые вызываются перед main (), чтобы инициировать фактическое соединение между вашим кодом C ++ и интерпретатором Python.

Я думаю, это не очень хорошо документировано в помощи boost :: python. Мне потребовалось некоторое время, чтобы понять это. Вначале я думал, что boost :: python каким-то образом (через некоторую магию шаблона) создает привязки во время компиляции. Если бы это было так, решение выше не сработало. Оглядываясь назад, теперь это все ясно.

Кроме того, это понимание (привязка выполняется во время выполнения, используя предоставленные вами определения) также дала мне идею написать статический класс регистра, который собирает классы для экспорта на Python. Это помогло бы моей библиотеке подойти еще дальше, поскольку мне не пришлось бы расширять основное определение BOOST_PYTHON_MODULE для каждого нового класса, но вместо этого просто зарегистрировать класс в статическом регистре (возможно, снова используя какой-то макрос …). Но это будущая работа … Проблема решена на данный момент!

  • непризнанная опция командной строки '-rdynamic' в GCC v4.9.2
  • Добавление скрипта python в проект c ++
  • Boost Python 1.48 сбой при экспорте 32-битного перечисления
  • Обработка изображений OpenCV - C ++ vs C vs Python
  • легкое преобразование с шестнадцатеричным / плавающим
  • Вызовите функцию python в C-коде
  • Является ли SSE невыровненной нагрузкой внутренней, чем медленная, чем выровненная нагрузка на процессоры Intel x64_64?
  • Простой IPC между C ++ и Python (кросс-платформа)
  • Python - лучший язык программирования в мире.