5

I am using Python 3.2 and C++.

I need to extract what kind of C type is currently being stored in a PyObject. I have checked over the documentation and googled it as well and no one else seems to have needed to do this.

So I have a PyObject and am attempting to extract the C Value. I have the list of functions need to actually extract the value from the object, but what I need to know first is what is being stored in the object itself as to call the correct function.

Just in case this helps understand here is some example code of what I am attempting.

//Variable is a custom variant type to allow for generic functionality.
Variable ExtractArgument( PyObject * arg )
{
  Variable value;
  PyObject* result;
  //now here is the problem, I need to know which Python function to call in order to
  //extract the correct type;
  value = PyLong_FromLong( arg );
  //or 
  value = PyFloat_FromDouble( arg )
  //ect.
  return value;
}

Hopefully I could have something that looks sort of like this

Variable ExtractArgument( PyObject * arg )
{
  Variable value;
  PyObject* result;
  //PyType is not the actual variable to that holds the type_macro, GetType should be
  //replaced by the function I am trying to find 
  PyType type = GetType( arg ); 
  switch( type )
  { 
    case T_INT: value = static_cast<int>(PyLong_FromLong( arg  )); 
      break;
    case T_FLOAT: value = static_cast<float>(PyFloat_FromDouble( arg  ));
      break;
    case T_DOUBLE: value = PyDouble_FromDouble( arg );
      break;
    //ect.
  }
  return value;
} 

Sorry if this question is to long, or too much info. First time post and didn't want to leave anything out that may help. Thank you for any help or insight you can give me on this issue.

greatwolf
  • 20,287
  • 13
  • 71
  • 105
DaneEC117
  • 227
  • 3
  • 10

2 Answers2

5

Python objects don't have a C type, they have a Python type. For example, an integer can be an C long or a long integer. You can check for the type with PyInt_Check(obj), PyList_Check(obj), etc. If that returns true then you know you have an object of that type.

Note that PyLong_FromLong and such go the other way. They take a C value and turn it into a PyObject*. So you're using them backwards. I think you meant PyInt_AsLong.

Adam
  • 16,808
  • 7
  • 52
  • 98
  • This appears so far to be the best option I have to attempt this with. I "might" run into some type conflicts because python doesn't store all the types I need so I will most probably be doing this in combination with a check on what type the C++ function is expecting. – DaneEC117 May 23 '11 at 23:08
  • Are you writing a Python wrapper for a C++ function? Sounds like you're re-inventing SWIG. – Adam May 23 '11 at 23:11
  • 1
    I am creating a simple to use python wrapper that creates what I call Bindable objects for my game engine. What it does it take the meta data of a class and cycles through this info and creates all the member function wrappers allowing our game designer to use python scripts to write gameplay using our C++ defined objects. The problem with things like SWIG and Boost::Python is that they are external programs that need to create the file before hand then you compile your program. For us this is done run time. The only change in compile will be adding a line of code to construct the class. – DaneEC117 May 23 '11 at 23:26
  • 3
    *The problem with Boost is that it is compile time and template hell. We like our game engine compiling in around 4 seconds and having almost no external dependencies, except things such as DirectX and Fmod. – DaneEC117 May 23 '11 at 23:27
1

That's not quite how Python types work; they don't map to C types one-to-one. There is no Python C/API function that will tell you what C type a particular value will fit in. There are, however, functions like PyFloat_Check() and PyInt_Check() that check the type (and consider subclasses as well.) There are also specifiers to PyArg_ParseTuple() (and its variants) that tell Python to convert a passed-in argument appropriately.

The usual thing to use is the latter; decode the arguments passed to a C function with PyArg_ParseTuple(), and if you need different types being passed treated differently, pass them as different arguments (usually as named arguments.) It's not clear if that approach can work for you. The second most usual thing to do is try to convert the argument using different functions without doing a typecheck first, and simply try another conversion function when one fails. The explicit typecheck is generally the least common alternative.

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • Yes, as I said, with `PyArg_ParseTuple()` and distinct types you need some way to differentiate the expected type beforehand. That's actually a very common theme in Python. Perhaps you want to change your class to actually store the expected C type as well as the PyObject value. Otherwise you run into issues where, for example, the int `1` is not a valid value for a `float` argument in your code, but is perfectly fine everywhere else. – Thomas Wouters May 23 '11 at 23:06
  • Well from what Adam said it looks like I will be doing type checks like PyInt_Check, PyFloat_Check, ect. Then after getting the true or false from this statement then check the variable I am using's meta type (which defines stuff like, int, unsigned, float, double, ect). Followed by the appropriate python call, such as PyInt_AsLong, PyFloat_AsDouble, ect. and an appropriate cast. – DaneEC117 May 23 '11 at 23:30
  • Yes, that's one of the things I described as well, and it has downsides as well (like the one I described.) – Thomas Wouters May 23 '11 at 23:35
  • Well I'm not actually calling the conversion and seeing it failed, I am calling the functions that both you and Adam pointed out and then calling the appropriate cast using my MetaData, because the Meta data that is passed into the constructor for my class holds information such as if the first argument is a double or an int, ect. So there really isn't to much of a downside to this, because for what we are storing I have a max of like 4 type checks, with one check on meta data followed by a cast (which is done run time) and what we are using python for isn't performance intensive. – DaneEC117 May 23 '11 at 23:48
  • I wasn't suggesting there was a performance problem. I was suggesting that it's usually quite confusing when, for example, the integer `1` isn't considered a valid float value in your specific usecase, when almost all of the Python code out there doesn't care at all. – Thomas Wouters May 23 '11 at 23:53