swig: расширение шаблона класса для обеспечения __str__

Скажем, у вас есть шаблонный класс Foo , и вы хотите его прозрачно обернуть Swig, чтобы вы могли напечатать класс:

 >>> from example import * >>> f = Foo2() >>> print(f) In Foo class! 

Я следил за этим сообщением и этим . Поэтому мой заголовочный файл:

 #include <iostream> template <int d> class Foo { public: friend std::ostream &operator<<(std::ostream &os, const Foo &m) { os << "Inside Foo class!" << std::endl; return os; } }; 

И мой файл интерфейса:

 %{ #include <sstream> #include <iostream> #include "foo.hpp" %} %include "std_iostream.i" // Try grabbing it unmodified %include "foo.hpp" /* Instantiate a few different versions of the template */ %template(Foo2) Foo<2>; %template(Foo3) Foo<3>; %extend Foo<2> { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } }; 

Так что это работает очень хорошо, я могу напечатать объект по-прежнему, но я хотел бы обобщить его для любого значения параметра шаблона, так как нет смысла копировать этот код для каждого параметра шаблона. Я попробовал следующее в файле интерфейса, но это не сработало:

 template <int d> class Foo { public: %extend { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } } }; 

Вы должны иметь возможность %extend основной шаблон, вне его определения, опустив список параметров шаблона:

 %extend Foo { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } }; %template(Foo2) Foo<2>; %template(Foo3) Foo<3>; 

Или вы можете использовать макрос SWIG для переноса и расширения каждой специализации за один раз:

 %define WRAP_FOO(N) %template( Foo ## N ) Foo<N>; %extend Foo<N> { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } }; %enddef /* Instantiate a few different versions of the template */ WRAP_FOO(2) WRAP_FOO(3) 

Обратите внимание, что в любом случае вы вызываете неопределенное поведение, возвращая результат .c_str() std::string которая уничтожается до возвращения функции.

Синтаксис %extend вы используете в своем последнем примере, должен быть правильным, это метод, который мы используем в OpenStudio

Я считаю, что проблема в том, что вы определяете шаблон 2 раза, один раз в своей директиве %import и один раз в своем .i файле. Первое определение – это то, которое использует SWIG.

Хотя это и не идеально, я считаю, что вам нужно будет отбросить директиву %include "foo.hpp" и определить конкретный интерфейс, который вы хотите. Ваш новый .i файл теперь станет чем-то вроде:

 %{ #include <sstream> #include <iostream> #include "foo.hpp" %} %include "std_iostream.i" // Don't let SWIG directly parse foo.hpp // %include "foo.hpp" template <int d> class Foo { public: // include here prototypes for all functions // you want exposed. You don't need the implementation like in // a normal C++ template declaration // include here any extensions you want to add %extend { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } } }; /* Instantiate a few different versions of the template */ %template(Foo2) Foo<2>; %template(Foo3) Foo<3>; 

Кроме того, вы можете поместить код SWIG в свой файл hpp напрямую и сохранить необходимость поддерживать два API:

Новый .i-файл:

 %{ #include <sstream> #include <iostream> #include "foo.hpp" %} %include "std_iostream.i" // let swig directly parse foo.hpp %include "foo.hpp" /* Instantiate a few different versions of the template */ %template(Foo2) Foo<2>; %template(Foo3) Foo<3>; 

Новый .hpp-файл:

 #include <iostream> template <int d> class Foo { public: #ifdef SWIG %extend { const char *__str__() { std::ostringstream oss(std::ostringstream::out); oss << *self; return oss.str().c_str(); } } #endif friend std::ostream &operator<<(std::ostream &os, const Foo &m) { os << "Inside Foo class!" << std::endl; return os; } };