Указатели на функции Boost.Python как аргумент конструктора класса - PullRequest
1 голос
/ 04 сентября 2010

У меня есть класс C ++, который требует указателя функции в своем конструкторе (float(*myfunction)(vector<float>*))
Я уже выставил некоторые указатели на функции в Python.
Идеальный способ использовать этот класс - это что-то вроде этого:

import mymodule
mymodule.some_class(mymodule.some_function)

Итак, я говорю Boost об этом классе так:

class_<SomeClass>("some_class", init<float(*)(vector<float>*)>);

Но я получаю:

error: no matching function for call to 'register_shared_ptr1(Sample (*)(std::vector<double, std::allocator<double> >*))'

при попытке его скомпилировать.

Итак, есть ли у кого-нибудь идеи о том, как я могу исправить ошибку, не теряя гибкости, полученной от указателей на функции (т. Е. Не возвращаясь к строкам, которые указывают, какую функцию вызывать)?

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

1 Ответ

2 голосов
/ 12 сентября 2010

ОК, так что это довольно сложный вопрос для ответа в целом.Основная причина вашей проблемы в том, что на самом деле нет типа python, который в точности эквивалентен указателю на функцию C.Функции Python довольно близки, но их интерфейс не совпадает по нескольким причинам.

Во-первых, я хочу упомянуть технику для переноса конструктора отсюда: http://wiki.python.org/moin/boost.python/HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29. Это позволяет вамнаписать функцию __init__ для вашего объекта, которая не соответствует непосредственно конструктору C ++.Также обратите внимание, что вам, возможно, придется указать boost::python::no_init в конструкции boost::python::class_, а затем def настоящую __init__ функцию позже, если ваш объект не может быть создан по умолчанию.

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

Если вы хотите предоставить общий подход, который будетработать для любого вызываемого питона, все становится сложнее.Вам придется добавить конструктор к вашему объекту C ++, который принимает общий функтор, например, используя boost::function или std::tr1::function.Вы можете заменить существующий конструктор, если хотите, потому что указатели на функции будут правильно преобразовываться в этот тип.

Итак, предполагая, что вы добавили конструктор boost::function в SomeClass, вы должны добавить эти функции к вашему.Код переноса в python:

struct WrapPythonCallable
{
    typedef float * result_type;
    explicit WrapPythonCallable(const boost::python::object & wrapped) 
        : wrapped_(wrapped)
    { }
    float * operator()(vector<float>* arg) const
    {
        //Do whatever you need to do to convert into a 
        //boost::python::object here
        boost::python::object arg_as_python_object = /* ... */;

        //Call out to python with the object - note that wrapped_ 
        //is callable using an operator() overload, and returns 
        //a boost::python::object.
        //Also, the call can throw boost::python::error_already_set - 
        //you might want to handle that here.
        boost::python::object result_object = wrapped_(arg_as_python_object);

        //Do whatever you need to do to extract a float * from result_object,
        //maybe using boost::python::extract
        float * result = /* ... */;
        return result;
    }
    boost::python::object wrapped_;
};

//This function is the "constructor wrapper" that you'll add to SomeClass.
//Change the return type to match the holder type for SomeClass, like if it's
//held using a shared_ptr.
std::auto_ptr<SomeClass> CreateSomeClassFromPython(
    const boost::python::object & callable)
{
    return std::auto_ptr<SomeClass>(
        new SomeClass(WrapPythonCallable(callable)));
}


//Later, when telling Boost.Python about SomeClass:
class_<SomeClass>("some_class", no_init)
    .def("__init__", make_constructor(&CreateSomeClassFromPython));

Я упустил подробности о том, как преобразовывать указатели в и из python - это, очевидно, то, что вам придется решить, потому что там возникают проблемы с временем жизни объекта.

Если вам нужно вызвать указатели функций, которые вы передадите этой функции из Python, то вам нужно def этих функций, используя Boost.Python в какой-то момент.Этот второй подход будет хорошо работать с этими определенными функциями, но их вызов будет медленным, потому что объекты будут излишне преобразовываться в и из Python при каждом вызове.

Чтобы исправить это, вы можете изменитьCreateSomeClassFromPython для распознавания известных или общих функциональных объектов и замены их действительными указателями функций.Вы можете сравнить идентичность объектов Python в C ++, используя object1.ptr() == object2.ptr(), что эквивалентно id(object1) == id(object2) в Python.

Наконец, вы, конечно, можете объединить общий подход с подходом enum.Имейте в виду, что при этом правила перегрузки boost :: python отличаются от правил C ++, и это может укусить вас при работе с такими функциями, как CreateSomeClassFromPython.Boost.Python тестирует функции в порядке их определения, чтобы увидеть, можно ли преобразовать аргументы времени выполнения в типы аргументов C ++.Таким образом, CreateSomeClassFromPython предотвратит использование конструкторов с одним аргументом def'd позже, чем он будет использоваться, потому что его аргумент соответствует любому объекту python.Обязательно ставьте его после других функций __init__ с одним аргументом.

Если вы обнаружите, что часто делаете подобные вещи, то вам, возможно, стоит взглянуть на общую технику boost :: functionна той же странице с именованным конструктором): http://wiki.python.org/moin/boost.python/HowTo?action=AttachFile&do=view&target=py_boost_function.hpp.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...