Boost :: Python - можно автоматически конвертировать из dict -> std :: map? - PullRequest
15 голосов
/ 25 мая 2011

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

class myClass {
    public:
    // Constructors - set a-f to default values.

    void SetParameters(std::map<std::string, double> &);
    private:
    double a, b, c, d, e, f;
}

void myClass::SetParameters(std::map<std::string, double> const& params) {
    // Code to iterate over the map, and set any found key/value pairs to their
    // corresponding variable.  i.e.- "a" --> 2.0, would set myClass::a to 2.0
}

В идеале, в Python я хотел бы выполнить это с помощью dict:

>>> A = myModule.myClass();
>>> A.SetParameters({'a': 2.2, 'd': 4.3, b: '9.3'})

Таким образом, пользователь можетвведите значения в любом порядке и введите любое их число для переопределения.Любые мысли о том, как это может быть достигнуто в boost :: python?Мне кажется, что я могу сделать это, изменив вход карты на объект boost :: python и используя функции extract.Однако это потребует от меня изменения интерфейса моей библиотеки (я бы предпочел сохранить интерфейс std :: map и использовать некоторую промежуточную / автоматическую конвертацию для версии Python).Мысли?

Ответы [ 3 ]

23 голосов
/ 25 мая 2011

Я думаю, что есть несколько способов, которые легче осуществить, чем написать собственный конвертер.Вы можете использовать boost :: python's map_indexing_suite, чтобы сделать преобразование для вас, или вы можете использовать ключевые аргументы в python.Я лично предпочитаю аргументы ключевых слов, так как это более «Pythonic» способ сделать это.

Так что это ваш класс (я добавил typedef для карты):

typedef std::map<std::string, double> MyMap;

class myClass {
public:
    // Constructors - set a-f to default values.

    void SetParameters(MyMap &);
private:
    double a, b, c, d, e, f;
};

Примерusing map_indexing_suite:

#include <boost/python/suite/indexing/map_indexing_suite.hpp>

using boost::python;

BOOST_PYTHON_MODULE(mymodule)
{
    class_<std::map<std::string, double> >("MyMap")
        .def(map_indexing_suite<std::map<std::wstring, double> >() );

    class_<myClass>("myClass")
        .def("SetParameters", &myClass::SetParameters);
}

Пример использования аргументов ключевого слова.Для этого необходимо использовать обёртку raw_function:

using namespace boost::python;

object SetParameters(tuple args, dict kwargs)
{
    myClass& self = extract<myClass&>(args[0]);

    list keys = kwargs.keys();

    MyMap outMap;
    for(int i = 0; i < len(keys); ++i) {
        object curArg = kwargs[keys[i]];
        if(curArg) {
            outMap[extract<std::string>(keys[i])] = extract<double>(kwargs[keys[i]]);
        }               
    }
    self.SetParameters(outMap);

    return object();
}

BOOST_PYTHON_MODULE(mymodule)
{
    class_<myClass>("myClass")
        .def("SetParameters", raw_function(&SetParameters, 1));
}

, что позволяет вам писать такие вещи на Python:

A.SetParameters(a = 2.2, d = 4.3, b = 9.3)
8 голосов
/ 25 мая 2011

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

struct SomeType_from_PyObject
{
    SomeType_from_PyObject();
    static void* convertible(PyObject* obj_ptr);
    static void construct(PyObject* obj_ptr,
                          converter::rvalue_from_python_stage1_data* data);
};

Если конструктор отвечает за добавление этого конвертера в реестр Boost.Python:

SomeType_from_PyObject::SomeType_from_PyObject()
{
    converter::registry::push_back(&convertible,
                                   &construct,
                                   type_id<SomeType>());
}

Функция convertible сообщает Boost, может ли этот преобразователь преобразовать указанный объект Python:

void* SomeType_from_PyObject::convertible(PyObject* obj_ptr)
{
    if (PyMapping_Check(obj_ptr)) {
        return obj_ptr;
    } else {
        return NULL;
    }
}

Функция construct фактически создает объект типа преобразования:

void SomeType_from_PyObject::construct(PyObject* obj_ptr,
                                       converter::rvalue_from_python_stage1_data* data)
{
    typedef converter::rvalue_from_python_storage<SomeType> storage_t;
    storage_t* the_storage = reinterpret_cast<storage_t*>(data);
    void* memory_chunk = the_storage->storage.bytes;
    object obj(handle<>(borrowed(obj_ptr)));
    SomeType* output = new (memory_chunk) SomeType();
    // Use the contents of obj to populate output, e.g. using extract<>
    data->convertible = memory_chunk;
}

и затем в вашей внутренней части вашего BOOST_PYTHON_MODULE добавьте строку

SomeType_from_PyObject();
0 голосов
/ 25 мая 2011

Я просто промочил пальцы ногами от boost :: python, поэтому не могу полностью ответить на ваш вопрос.Но первый контрольно-пропускной пункт, который я вижу, - это гарантия того, что ключи от py dict являются строками.Диктофоны Python также могут быть привязаны к кортежам (и я предполагаю, что больше типов).

...