How to set values of py::dict in C++? - python

I want to use a py::dict from C++. But operator[] does not seem to be defined, and I can't find any information here or in the pybind11 docs of how to add a key/value pair or return a value for a key?
edit: Maybe also important to mention I've got integers as keys.
edit2: Needed to use py::int_()

I see operator[] defined for py::dict, example:
m.def("test", [](){
py::dict d;
d[py::int_{0}] = "foo";
return d;
});
>>> example.test()
{10: 'foo'}

You can see operator[] has two overloads takes either a py::handle or string literal, so d["xxx"] or d[py::int_{0}] work but not d[0] (which would be wrongly resolved as a invalid string literal at compile time,and would cause run-time segment-fault)
template <typename Derived>
class object_api : public pyobject_tag {
...
/** \rst
Return an internal functor to invoke the object's sequence protocol. Casting
the returned ``detail::item_accessor`` instance to a `handle` or `object`
subclass causes a corresponding call to ``__getitem__``. Assigning a `handle`
or `object` subclass causes a call to ``__setitem__``.
\endrst */
item_accessor operator[](handle key) const;
/// See above (the only difference is that they key is provided as a string literal)
item_accessor operator[](const char *key) const;
also you cannot use std::string as key:
std::string key="xxx";
d[key] = 1; // failed to compile, must change to d[pybind11::str(key)]
to make things easier, use pybind11::cast() to explicitly convert any supported C++ type into corresponding python type, as following:
std::string key="xxx";
d[pybind11::cast(1)] = 2
d[pybind11::cast(key)] = 3

Related

How to apply a SWIG typemap for a double pointer struct argument

I have an API that I am trying to wrap using SWIG such that I can call the underlying C library from python.
I have got stuck with a particular API fn:
int update_tracks(track_t **phash_tracks,
const pdws_t *pdw_frame,
const rdws_t *rdw_frame,
lib_t *lib,
lib_meta_t *lib_meta,
const cfg_t *cfg);
Its the double pointer to track_t data structure that I can't handle.
All the single pointers work fine.
This is the only API fn that has a double pointer to track_t
All the others only have a single pointer, e.g.
void print_hash_tracks(const track_t *hash_tracks, const cfg_t *cfg,
enum TRKTYPE trktype);
I'm pretty sure I need to make a typemap in my SWIG interface file (interface.i) but I am finding the SWIG docs impenetrable.
What I think I need to do is create a typemap that whenever it sees the track_t** type, it takes a track_t* and converts it to its address, something like:
/* provide typemap to handle instances of track_t** parameters */
%typemap(in) track_t** (track_t *tracks) {
$1 = &tracks;
}
but I'm just getting segmentation faults when I run:
tracks = g3.track_t()
g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
on the python side.
I feel like I've almost solved this but can't quite get the typemap specification right and at the same time struggling to understand the relevant documentation.
flexo - if you're out there - maybe you can shed some light on this, you seem to be the SO expert in this area..
UPDATE - m7ython (brilliant! another SWIG expert on SO)
Usage in C is pretty straigthforward
declare and initialise a track_t pointer to NULL:
track_t *hash_tracks = NULL;
then:
update_tracks(&hash_tracks, &pdw_frame, &rdw_frame,
&lib, &lib_meta, &cfg);
So the address of the pointer to track_t is passed as an arg to update_tracks(). The update_tracks() fn takes care of all the necessary mallocs for the data that gets put into hash_tracks, i.e. the hash table of track_t structs
All the other args are single pointers and I can create and populate them with no issues on the python side.
track_t is a struct containing a bunch of ints, floats, char* etc. e.g.
typedef struct
{
/* make struct hashable */
UT_hash_handle hh;
int id;
...
char name[MAX_BUF];
...
} track_t;
The reason that the track_t arg is a track_t** and not just a track_t* is because hash_tracks is a pointer to a hash table (using the UTHash library). hash_tracks points to the 1st track_t in the hash table. In the body of the update_tracks() fn track_t structs can be added/removed from the hash table, such that the pointer to the 1st track_t may change, i.e. hash_tracks may point to something else after the call to update_tracks(), hence the reason for passing a pointer to the pointer.
In other words, the track_t** arg, phash_tracks is being used both as an input and output type arg, hence the pointer to a pointer. All the other args are simply inputs, they don't change so they can be passed in as single pointers.
I attempted the 'helper fn' route with the following C fn:
track_t** make_phash_tracks(void)
{
track_t **phash_tracks;
phash_tracks = calloc(1, sizeof(track_t*));
return phash_tracks;
}
the use of calloc should ensure that *phash_tracks is NULL
this compiled and wrapped with no errors, but when I used it from the python side it segfaulted, e.g.
phash_tracks = g3.make_phash_tracks()
g3.update_tracks(phash_tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
checking the phash_tracks var just prior to calling update_tracks gave:
(Pdb) p phash_tracks
<Swig Object of type 'track_t **' at 0x7fb9e37c9030>
EDIT: Ok, I think I now understand what update_tracksdoes. It seems you can use the function in two ways. Either to update existing tracks, or to create tracks if you pass a pointer to a NULL pointer. I am not sure about the most elegant way to handle both cases in SWIG (or if this is even a problem), but here are some options.
1. phash_tracks is an output argument
First, you must pass *phash_tracks back to Python as a return value, and use the function in some form like
>>> int_res, tracks = g3.update_tracks(tracks, pdw_frame, rdw_frame, lib, lib_meta, cfg)
or
>>> int_res, tracks = g3.update_tracks(pdw_frame, rdw_frame, lib, lib_meta, cfg)
This is accomplished by the following "argout" typemap:
%typemap(argout) track_t **phash_tracks {
%append_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*1_descriptor, SWIG_POINTER_OWN));
}
Maybe you don't want Python to take ownership of the track_t*, then replace SWIG_POINTER_OWN by 0.
2. Passing an empty phash_tracks
If you only want to use the update_tracks function to create tracks, you can do essentially what you are already doing. Use the following "in" typemap, and use the function as in the second example above (without the tracks parameter).
%typemap(in, numinputs=0) track_t **phash_tracks (track_t *tracks) {
tracks = NULL;
$1 = &tracks;
}
3. phash_tracks as an input (and output) argument
If you want to use update_tracks to update existing tracks, you should be able to use the "in" typemap I suggested before, and use the function from Python as in the first example (including the tracks parameter).
%typemap(in) track_t **phash_tracks (track_t *tracks) {
if ((SWIG_ConvertPtr($input, (void **) &tracks, $*1_descriptor, SWIG_POINTER_EXCEPTION | SWIG_POINTER_DISOWN)) == -1)
return NULL;
$1 = &tracks;
}
Note that it is important that Python disowns its tracks_t*.
4. Enabling both (2) and (3) above
You could basically use version (3) also to create tracks, if you could get swig to pass a wrapped NULL tracks_t*. I am not sure if SWIG allows this -- but maybe it does. Try using a helper function:
tracks_t* empty_tracks() { return NULL; }
Alternatively, you can modify the "in" typemap along the following lines, attempting to convert the provided argument to a track_t* and passing its address, or alternatively passing the address of a NULL track_t*.
%typemap(in) track_t **phash_tracks (track_t *tracks) {
// Alternatively, check if $input is a 0 integer `PyObject`...
if ((SWIG_ConvertPtr($input, (void **) &tracks, $*1_descriptor, SWIG_POINTER_DISOWN)) == -1)
tracks = NULL;
$1 = &tracks;
}
Then, from Python, just pass something else to create tracks:
>>> int_res, tracks = g3.update_tracks(0, pdw_frame, rdw_frame, lib, lib_meta, cfg)

Wrapping a C function that expect C dynamic callbacks

I am trying to write a wrapper around libedit (a BSD alternative to readline with a slightly different API) and there is a way to add a named function to the C API.
For example in C:
static unsigned char show_help(EditLine *e, int ch) {
printf("Help");
}
el = el_init(argv[0], stdin, stdout, stderr);
el_set(el, EL_ADDFN, "help", "This is help", show_help);
el_set(el, EL_BIND, "\?", "help", NULL);
I call el_set to add a function and then bind that function later on.
I can't find a good way to allow me to wrap EL_ADDFN to bind Python methods dynamically. I could create a bunch of prenamed C function and wrap them all individually to python methods, but I would rather like emulate the C API as closely as possible.
Is there a way to call EL_ADDFN and determine which python method it is calling?
Try this: One single handler function (which I'll describe below). Wrap EL_ADDFN so that it records the mapping of name to python function, but always uses the one handler function. Wrap EL_BIND, so that it records the mapping of character to function name. Your handler function should look up the ch parameter in your character to name mapping and then lookup the name to function mapping and then call the function. (If ADDFN must be called before BIND, you could create a map of ch to function and populate that directly in the the BIND wrapper.)
In pseudo C:
const char *chmap[256]; // initialize to zero
struct hashtable *namemap; // up to you to find a
// hashtable implementation that
// will take const char * and map to
// PyObject * (function object);
static unsigned char python_func(EditLine *e, int ch) {
const char *name = chmap[ch];
// check for errors
PyObject *func = lookup(namemap, name);
// check for errors
PyObject *editline = convert(e); // or whatever you have
PyObject *result = PyObject_CallFunctionObjArgs(func, NULL);
// check result, convert to unsigned char, and return
}
So, ADDFN wrapper populates the hashtable, and the BIND operator populates the chmap.

Passing an object to C module, in Python

I ran into a situation with pure python and C python module.
To summarize, how can I accept and manipulate python object in C module?
My python part will look like this.
#!/usr/bin/env python
import os, sys
from c_hello import *
class Hello:
busyHello = _sayhello_obj
class Man:
def __init__(self, name):
self.name = name
def getName(self):
return self.name
h = Hello()
h.busyHello( Man("John") )
in C, two things need to be resolved.
first, how can I receive object?
second, how can I call a method from the object?
static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
PyObject *obj;
// How can I fill obj?
char s[1024];
// How can I fill s, from obj.getName() ?
printf("Hello, %s\n", s);
return Py_None;
}
To extract an argument from an invocation of your method, you need to look at the functions documented in Parsing arguments and building values, such as PyArg_ParseTuple. (That's for if you're only taking positional args! There are others for positional-and-keyword args, etc.)
The object you get back from PyArg_ParseTuple doesn't have it's reference count increased. For simple C functions, you probably don't need to worry about this. If you're interacting with other Python/C functions, or if you're releasing the global interpreter lock (ie. allowing threading), you need to think very carefully about object ownership.
static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
PyObject *obj = NULL;
// How can I fill obj?
static char fmt_string = "O" // For "object"
int parse_result = PyArg_ParseTuple(args, fmt_string, &obj);
if(!parse_res)
{
// Don't worry about using PyErr_SetString, all the exception stuff should be
// done in PyArg_ParseTuple()
return NULL;
}
// Of course, at this point you need to do your own verification of whatever
// constraints might be on your argument.
For calling a method on an object, you need to use either PyObject_CallMethod or PyObject_CallMethodObjArgs, depending on how you construct the argument list and method name. And see my comment in the code about object ownership!
Quick digression just to make sure you're not setting yourself up for a fall later: If you really are just getting the string out to print it, you're better off just getting the object reference and passing it to PyObject_Print. Of course, maybe this is just for illustration, or you know better than I do what you want to do with the data ;)
char s[1024];
// How can I fill s, from obj.getName() ?
// Name of the method
static char method_name = "getName";
// No arguments? Score! We just need NULL here
char method_fmt_string = NULL;
PyObject *objname = PyObject_CallMethod(obj, obj_method, method_fmt_string);
// This is really important! What we have here now is a Python object with a newly
// incremented reference count! This means you own it, and are responsible for
// decrementing the ref count when you're done. See below.
// If there's a failure, we'll get NULL
if(objname == NULL)
{
// Again, this should just propagate the exception information
return NULL;
}
Now there are a number of functions in the String/Bytes Objects section of the Concrete Objects Layer docs; use whichever works best for you.
But do not forget this bit:
// Now that we're done with the object we obtained, decrement the reference count
Py_XDECREF(objname);
// You didn't mention whether you wanted to return a value from here, so let's just
// return the "None" singleton.
// Note: this macro includes the "return" statement!
Py_RETURN_NONE;
}
Note the use of Py_RETURN_NONE there, and note that it's not return Py_RETURN_NONE!
PS. The structure of this code is dictated to a great extent by personal style (eg. early returns, static char format strings inside the function, initialisation to NULL). Hopefully the important information is clear enough apart from stylistic conventions.

Boost.Python - How to return by reference?

I'm using Boost.Python to create Python modules from C++ classes. And I ran into a problem with references.
Condider the following case where I have a class Foo with overloaded get methods that can either return by value or reference.
Specifying that the return by value should be used was easy once I typedefed a signature. But I think it should be possible return a reference as well by using a return_value_policy. However, using what seemed appropriate (doc); return_value_policy<reference_existing_object> did not seem to work.
Have I misunderstood what it does?
struct Foo {
Foo(float x) { _x = x; }
float& get() { return _x; }
float get() const { return _x; }
private:
float _x;
};
// Wrapper code
BOOST_PYTHON_MODULE(my_module)
{
using namespace boost::python;
typedef float (Foo::*get_by_value)() const;
typedef float& (Foo::*get_by_ref)();
class_<Foo>("Foo", init<float>())
.def("get", get_by_value(&Foo::get))
.def("get_ref", get_by_ref(&Foo::get),
return_value_policy<reference_existing_object>())//Doesn't work
;
}
Note: I know it could be dangerous to reference existing object without life-time managing.
Update:
It looks like it works for objects but not basic data types.
Take this revised example:
struct Foo {
Foo(float x) { _x = x; }
float& get() { return _x; }
float get() const { return _x; }
void set( float f ){ _x = f;}
Foo& self(){return *this;}
private:
float _x;
};
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
typedef float (Foo::*get_by_value)() const;
class_<Foo>("Foo", init<float>())
.def("get", get_by_value(&Foo::get))
.def("get_self", &Foo::self,
return_value_policy<reference_existing_object>())
.def("set", &Foo::set);
;
}
Which in a test gave the expected result:
>>> foo1 = Foo(123)
>>> foo1.get()
123.0
>>> foo2 = foo1.get_self()
>>> foo2.set(1)
>>> foo1.get()
1.0
>>> id(foo1) == id(foo2)
False
In Python, there's the concept of immutable types. An immutable type can't have its value changed. Examples of built-in immutable types are int, float and str.
Having said that, you can't do what you want with boost::python, because Python itself does not allow you to change the value of the float returned by the function.
Your second example shows one solution, another would be to create thin-wrappers and exposing that:
void Foo_set_x(Foo& self, float value) {
self.get() = value;
}
class_<Foo>("Foo", init<float>())
...
.def("set", &Foo_set_x);
;
Which is a better solution than having to change the original C++ class.
I think you want return internal reference instead. I have used it before to do something similar.
Edit: Latest doc
I don't know much about Boost.Python, so I may misunderstand the question, in which case this is completely unhelpful. But here goes:
In Python you can't choose between returning by reference or by value, the distinction doesn't make sense in Python. I find it's easiest to think of it as everything being handled by reference.
You just have objects, and you have names for those objects. So
foo = "ryiuy"
Creates the string object "ryiuy" and then lets you refer to that string object with the name "foo". So in Python, when you get passed something, you get passed that object. There is no "values" as such, so you can't pass the value. But then again, it's also a valid viewpoint that there aren't references either, just objects and their names.
So the answer is, I guess, is that when you get a reference in C, you need to pass a reference to the object that reference references into Python. And when you get a value in C, you need to pass a reference to the object that you create from that value into Python.
Are you sure that the c++ object is being copied? You will get a new python object each time but which references the same c++ object. How are you determining that the object has been copied?

problems Wrapping Patricia Tries using Swig, python

I'm trying to wrap the Patricia Tries (Perl's NET::Patricia) to be exposed in python. I am having difficulty with one of the classes.
So instances the patricia node (below) as viewed from python have a "data" property. Reading it goes fine, but writing to it breaks.
typedef struct _patricia_node_t {
u_int bit; /* flag if this node used */
prefix_t *prefix; /* who we are in patricia tree */
struct _patricia_node_t *l, *r; /* left and right children */
struct _patricia_node_t *parent;/* may be used */
void *data; /* pointer to data */
void *user1; /* pointer to usr data (ex. route flap info) */
} patricia_node_t;
Specifically:
>>> N = patricia.patricia_node_t()
>>> assert N.data == None
>>> N.data = 1
TypeError: in method 'patricia_node_t_data_set', argument 2 of type 'void *'
Now my C is weak. From what I read in the SWIG book, I think this means I need to pass it a pointer to data. According to the book :
Also, if you need to pass the raw pointer value to some external python library, you can do it by casting the pointer object to an integer... However, the inverse operation is not possible, i.e., you can't build a Swig pointer object from a raw integer value.
Questions:
am I understanding this correctly?
how do I get around this? Is %extends? typemap? Specifics would be very helpful.
Notes:
I can't change the C source, but I can extend it in additional .h files or the interface .i file.
From what I understand, that "data" field should be able to contain "anything" for some reasonable value of "anything" that I don't really know.
I haven't used SWIG in a while, but I am pretty sure that you want to use a typemap that will take a PyObject* and cast it to the required void* and vice versa. Be sure to keep track of reference counts, of course.
It looks like you should pass SWIG a pointer to an integer. For example, if this was all in C, your error would be like this:
void set(struct _patricia_node_t *tree, void *data) {
tree->data = data;
}
...
int value = 1;
set(tree, &value); // OK! HOORAY!
set(tree, value); // NOT OK! FIRE SCORPIONS!
And it seems to me you're doing the Python equivalent of set(tree, value). Now I'm not an expert with SWIG but perhaps you could pass a tuple instead of an integer? Does N.data = (1,) work? This was the answer suggested by an Allegro CL + SWIG example, but I dunno how well it applies to Python.
An alternative is use PyRadix, which uses the same underlying code.

Categories