Boost:: Python - possibilité de convertir automatiquement depuis dict -- > std:: map?

j'ai une Classe C++, avec une fonction de membre qui peut prendre un petit à un grand nombre de paramètres. Appelons ces Paramètres, A-F. Tous les paramètres ont des valeurs par défaut. Dans le cadre du projet python sur lequel je travaille, je veux exposer cette classe à python. Actuellement, la fonction de membre ressemble à quelque chose comme ceci:

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
}

idéalement, en Python, je voudrais accomplir ceci en utilisant un dict:

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

De cette façon, le l'utilisateur peut entrer les valeurs dans n'importe quel ordre, et entrer n'importe quel nombre d'entre eux à être dépassé. Des idées sur la façon dont cela pourrait être accompli dans boost:: python? Il me semble que je peux le faire en changeant l'entrée map en boost::Python object, et en utilisant les fonctions d'extraction. Cependant, cela me demanderait de changer l'interface de ma bibliothèque (je préférerais garder l'interface std::map, et avoir une technique de conversion intermédiaire/automatique pour la version python). Pensées?

15
demandé sur MarkD 2011-05-25 00:19:50

3 réponses

je pense qu'il y a plusieurs façons plus faciles à accomplir que d'écrire votre propre convertisseur. Vous pouvez utiliser boost::Python map_indexing_suite pour faire la conversion pour vous, ou vous pouvez utiliser des arguments de mots-clés en python. Personnellement, je préfère les arguments de mots-clés, car c'est la façon plus "pythonique" de le faire.

donc c'est votre classe (j'ai ajouté un typedef pour la carte):

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;
};

exemple utilisant 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);
}

exemple utilisant des arguments de mots clés. Cela nécessite l'utilisation d'un emballage 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));
}

cela vous permet d'écrire des choses comme ça en Python:

A.SetParameters(a = 2.2, d = 4.3, b = 9.3)
23
répondu Aleksey Vitebskiy 2011-05-25 02:40:28

ce billet de blog a une description assez claire de la façon d'écrire ces convertisseurs. Le modèle de base est de définir une classe qui a la forme:

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);
};

où le constructeur est responsable d'ajouter ce convertisseur pour Boost.Python registre:

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

la fonction convertible indique à Boost si ce convertisseur peut convertir l'objet Python spécifié:

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

la fonction construct crée en fait un objet du type de conversion:

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;
}

et ensuite dans votre intérieur votre BOOST_PYTHON_MODULE, incluez la ligne

SomeType_from_PyObject();
7
répondu Ray 2011-05-24 21:32:32

je viens de me faire mouiller les orteils avec boost::python ne peut donc pas répondre complètement à votre question. Mais le premier obstacle que je vois est de garantir que les clés du Py dict sont toutes des cordes. Python dicts peut également être saisi sur tuples (et je suppose plus de types).

0
répondu totowtwo 2011-05-24 21:26:52