Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python C API: Switch on PyObject type

I have some code to interface Python to C++ which works fine but every time I look at it I think there must be a better way to do it. On the C++ side there is a 'variant' type that can deal with a fixed range of basic types - int, real, string, vector of variants, etc. I have some code using the Python API to convert from the equivalent Python types. It looks something like this:

variant makeVariant(PyObject* value)
{  
  if (PyString_Check(value)) {
    return PyString_AsString(value);
  }
  else if (value == Py_None) {
    return variant();
  }
  else if (PyBool_Check(value)) {
    return value == Py_True;
  }
  else if (PyInt_Check(value)) {
    return PyInt_AsLong(value);
  }
  else if (PyFloat_Check(value)) {
    return PyFloat_AsDouble(value);
  }
  // ... etc

The problem is the chained if-else ifs. It seems to be calling out for a switch statement, or a table or map of creation functions which is keyed by a type identifier. In other words I want to be able to write something like:

  return createFunMap[typeID(value)](value);

Based on a skim of the API docs it wasn't obvious what the best way is to get the 'typeID' here directly. I see I can do something like this:

  PyTypeObject* type = value->ob_type;

This apparently gets me quickly to the type information but what is the cleanest way to use that to relate to the limited set of types I am interested in?

like image 646
Bob Avatar asked Jun 22 '10 11:06

Bob


1 Answers

In a way, I think you've answered your own question.

Somewhere, you're going to have to select functionality based on data. The way to do this in C is to use function pointers.

Create a map of object_type->function mappers... where each function has a clearly-defined interface.

variant PyBoolToVariant(PyObject *value) {
    return value == Py_True;
}

Map<PyTypeObject*,variant* (PyObject*)> function_map;

function_map.add(PyBool, PyBoolToVariant);

Now your makeVariant can look like this.

variant makeVariant(PyObject *value) {
    return (*function_map.get(value->ob_type))(value);
}

The hard part is going to be getting the syntax right for the Map object. Also, I'm assuming there is a Map object you can use that takes type parameters (<PyTypeObject*, variant*(PyObject*)).

I probably have not quite gotten the syntax correct for the second type of the Map. It should be a pointer to a function which takes one pointer and returns a pointer to a variant.

I hope this is helpful.

like image 73
Jason R. Coombs Avatar answered Oct 21 '22 03:10

Jason R. Coombs