2013-06-24 4 views
5

По-видимому, SWIG не понимает std::function и разбивает привязки Python. Например, это работает в C++:Обработка std :: function с SWIG

// Somewhere in the API 
typedef std::function<void(const UnitError & error)> UnitErrorHandler; 

// Somewhere else in the API 
void Unit::setErrorHandler(const UnitErrorHandler & handler) {} 

// In the application code 
unit->setErrorHandler([](const UnitError & error){ 
    std::cerr << error << std::endl; 
    std::exit(1); 
}); 

Но это нарушит код (кроме имеющих различное поведение для простоты, но это не точка):

unit.setErrorHandler(lambda error: len(error)) 

ситуация то же самое с def (обычными) несвязанными функциями. Итак, кто-нибудь знает обходное решение для этого?

+1

Не уверен, что это вариант, но это было бы «относительно» легко сделать с помощью Boost Python. –

+0

См. Мой ответ на http://stackoverflow.com/a/11522655/168175, который фактически использовал 'std :: function' в качестве примера. – Flexo

ответ

1

std::function относительно новый (C++ 11), поэтому у SWIG нет никакого готового решения для него. Хотя это не так гибко, вы можете использовать с указателями функций, с оговоркой. Цитирование 5.4.9 Pointers to functions and callbacks в docs:

... SWIG обеспечивает полную поддержку для указателей функций при условии, что функции обратного вызова определяются в C, а не на целевом языке.

Так что прохождение лямбда не сработает. Прочтите ссылку на документацию для некоторых альтернатив.

1

В двух строках: создайте функцию интерфейса, которая принимает команду в виде строки и устанавливает функтор, который затем присваивается std::function. operator() позаботится об оценке строки на целевом языке.


Давайте предположим, что в файле заголовка mymodule.hh у вас есть класс MyModule с методом void MyModule::Func(const std::function<double(Bunch&)> & func) где Bunch является другой объект.

Это можно решить, определяя функцию интерфейса, которая принимает const char *, представляющую выражение для eval, и переносит его в функтор, который затем будет присвоен std::function. Хорошая новость заключается в том, что это может полностью сделано в файле интерфейса Swig, не касаясь кода C++, вот как (я использовал Tcl, вы просто должны адаптировать operator()):

%module mymodule 
%{ 
    #include "bunch.hh" 
    #include "mymodule.hh" 

    extern Tcl_Interp* tcl_interp; 

    struct Tcl_bunch_callback { 
    std::string callback; 
    double operator()(Bunch & bunch) 
    { 
     Tcl_Obj * bunch_obj = SWIG_Tcl_NewInstanceObj(tcl_interp, &bunch, SWIGTYPE_p_Bunch, /*own pointer?*/0); 
     Tcl_SetVar2Ex(tcl_interp, "bunch", (const char*)nullptr, bunch_obj, 0); 
     double resultValue; 
     const int resultCode = Tcl_ExprDouble(tcl_interp, callback.c_str(), &resultValue); 
     if (resultCode != TCL_OK) { 
     std::cerr << "WARNING evaluation of tcl expression failed: " 
        << Tcl_GetStringResult(tcl_interp) << std::endl; 
     resultValue = max_double; 
     } 
     Tcl_DeleteCommand(tcl_interp, Tcl_GetString(bunch_obj)); //remove the used command to avoid leaks 
     return resultValue; 
    } 
    }; 
%} 

%include "bunch.hh" 
%include "mymodule.hh" 
%extend MyModule 
{ 
    void Func(const char * cmd) { 
    $self->Func(std::function<double(Bunch&)>(Tcl_bunch_callback(cmd))); 
    } 
} 

В моем случае operator() довольно ограничена Tcl, но я уверен, что подобные процедуры могут быть написаны и для других целевых языков. Следуйте за некоторыми подробностями.

Я предоставляю пользователю возможность доступа к методам текущего Bunch, обрабатываемых на C++ непосредственно из Tcl. Функция: SWIG_NewInstanceObj позволяет преобразовать указатель Bunch в его представление на целевом языке и создать экземпляр его в интерпретаторе (эта функция не документирована, но копает бит в любом сгенерированном swig файле обложек, это не так сложно понять его механизм). С помощью следующей команды я установил этот объект в переменную с именем bunch, чтобы она стала доступной пользователю просто с $bunch, и все методы, экспортированные с помощью swig, затем доступны.

Я думаю, это потрясающе, как такие мощные вещи доступны с такими небольшими строками кода благодаря swig!