2012-07-05 2 views
2

Я создаю интерфейс Swig для шаблонного класса. В моем pyinterface.i файла, я объявляюswig расширяет шаблонный класс с переменной

%template (myclass) MyClass<int>; 

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

%extend MyClass<int>{ 
    double x; 
} 

Однако это жалуется, что нет

myclass_get_x

метод определен. Поэтому, если я попытаюсь определить это, изменив приведенное выше значение:

%extend MyClass<int>{ 
    double x; 
    double MyClass<int>_get_x(MyClass<int> *f){ 
      return (*f)->x; 
    } 
} 

Затем я получаю синтаксические ошибки.

Я попытался сделать это как:

%extend myclass{ 
    double x; 
    double myclass_get_x(myclass *f){ 
      return (*f)->x; 
    } 
} 

Но это также бросает ошибки, так как MyClass не кажется, пока понятны.

+0

Не связано с моим ответом, но является ли 'myclass * f' означающим« указатель »здесь? Вам не нужно явно писать этот аргумент, но вам нужно использовать '$ self' вместо' this' с '% extend'. – Flexo

+0

Да, это, по сути, то, как я пытался его использовать. Я решил, что этот формат был альтернативой использованию «$ self» вместо «this». Возможно, я не могу этого сделать. – thebigdog

ответ

1

Когда вы расширяете класс с помощью %extend, вы не можете добавить новые переменные-члены, а только функции-члены. Он предположит существование функции get/set, если вы сделаете это в %extend.

Причина в том, что память (то есть память) для новых переменных-членов должна была бы где-то жить, но нет очевидного безопасного места для ее размещения. Вы не можете изменить исходный C++ class, потому что если вы это сделаете, вы получите неопределенное поведение, когда какой-либо другой, предварительно скомпилированный код использует старое определение класса; нет никакого способа ретроспективно применить новое определение. Вы не можете поместить его в прокси-сервер, который генерируется на целевом языке, потому что не существует сопоставления 1: 1 между экземплярами классов C++ и прокси-серверов на целевом языке. (Рассмотрим две функции, которые возвращают один и тот же глобальный экземпляр через указатель для тривиального примера того, как это может произойти).

Если вы хотите реализовать функции get/set, чтобы сделать что-то полезное (например, здесь я использовал глобальную карту для хранения дополнительных данных), которую вы не хотите реализовывать внутри %extend Например, данный test.hh с просто:

template <typename T> 
struct Foo {}; 

вы можете использовать std::map делать то, что вы пытаетесь сделать, написав свой собственный получает и устанавливает как свободные функции в обертке - %{ %} просто передает код прямо через:

%module test 

%include "test.hh" 

%{ 
#include "test.hh" 
%} 

%{ 
#include <map> 
static std::map<Foo<int>*, double> extra_stuff; 

const double& Foo_Sl_int_Sg__x_get(Foo<int>* f) { 
    return extra_stuff[f]; 
} 

void Foo_Sl_int_Sg__x_set(Foo<int>* f, const double& d) { 
    extra_stuff[f] = d; 
} 
%} 

%template(FooInt) Foo<int>; 

%extend Foo<int> { 
    double x; 
} 

GE t/set функции искажаются собственной системой управления SWIG, потому что это шаблон. Я просто посмотрел в сгенерированный класс-оболочку, чтобы узнать, что они вызвали. (Я думаю, что может быть более умный способ сделать это, но я не мог понять это из документации). Если бы это был не шаблон, имя функций было бы намного проще.

Обратите внимание, что здесь нет никаких положений, позволяющих удалять записи с этой карты - он будет просто расти бесконечно. (Вы можете обойти это, например, путем предоставления типовой карты, которая вызывает дополнительную функцию в конце жизни объекта). Вы также должны быть осторожны в отношении потоков.

Поскольку вы не указали, какой язык вы используете я тестировал выше кода Java:

public class run { 
    public static void main(String[] argv) { 
    System.loadLibrary("test"); 
    FooInt f = new FooInt(); 
    f.setX(0.1); 
    System.out.println(f.getX()); 
    } 
} 

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

Простейшим обходным решением во многих случаях (например, итераторами на целевом языке) является использование %inline, чтобы объявить и определить и полностью обернуть все совершенно новый дополнительный класс, который обеспечивает требуемые функции.

+0

Спасибо Flexo, отличный ответ! Многому научился из того, что я предполагал, это вопрос типа «ты забыл запятую». – thebigdog

+0

Я смущенно об одном: ** Вы не можете поместить его в прокси-сервер, который генерируется на целевом языке, потому что не существует сопоставления 1: 1 между экземплярами классов C++ и прокси-серверов на целевом языке , (Рассмотрим две функции, которые возвращают один и тот же глобальный экземпляр с помощью указателя для тривиального примера того, как это может произойти). ** Можете ли вы предоставить ссылку, чтобы дополнительно объяснить отсутствие сопоставления 1: 1? Это довольно неожиданно для меня из того, что я до сих пор читал о свинге. Я уверен, что ты прав, я просто не знаю, почему. – thebigdog

+0

@thebigdog SWIG работает только с определениями функций, а не деклараций. Поэтому трудно доказать, что функция может возвращать одну и ту же вещь дважды или нет. Вот почему вам нужно использовать '% newobject', чтобы сообщить SWIG о семантике свойств функций, которые создают новые объекты. Если прокси не принадлежит этому объекту, тогда может быть несколько созданных за время жизни объекта. В действительности очень сложно доказать, что что-то является одним и тем же объектом - если вы вызываете 'new', а затем' delete', а затем 'new' снова, чтобы законно использовать один и тот же адрес. – Flexo