Iterative argument conversion in C extensions

James Henstridge james+news at daa.com.au
Mon Jan 31 02:32:20 EST 2000


If all you want to do is get at the arguments directly, remember that
the arguments are passed in as a tuple, so you can use the
PyTuple_GetItem function to get the arguments, and use PyInt_Check,
PyString_Check, etc to check the type of an argument.

If you are trying to wrap a C varargs function, this is a little more
difficult.  Here is a bit of info that may help though:

There is no portable way to do this such that it will work on all
platforms (that I know of).  If you don't need platform independence,
you could find out about the calling convention of the platform you are
using and write a bit of assembly code to construct an argument vector
on the stack.  You would have to do this for all platforms you want to
use.  This is the sort of solution Netscape is using to allow javascript
to call XPCOM methods in mozilla.

There are other ways of getting this sort of behaviour that will work in
most cases for a limited number of argument types though.  Just create a
prototype something like this:
  typedef void (*lotsofargs) (sometype *non_varargs_arg,
                              void *arg1,
                              void *arg2,
                              ...
                              void *argn);

(n is sufficiently large to hold the maximum number of arguments you
will ever want to pass -- not quite varargs, but it will probably do).

For each string and int argument, cast it to (void *).  Handling doubles
is more difficult though.  On 32 bit architectures, they are usually
twice as big as a pointer, so they are more difficult to support.

One possible way to handle doubles (which I haven't tested) is with a
function something like this:
  void
  _split_double(void *arg1, void *arg2, void **ret1, void **ret2)
  {
    *ret1 = arg1;
    *ret2 = arg2;
  }
  typedef void (*split_double_type)(double arg, void **ret1, void
**ret2);
  split_double_type split_double = (split_double_type)_split_double;

On machines with 32 bit alignment and sizeof(double)==2*sizeof(void*),
this should split the double argument into two args you can pass to
lotsofargs.  You would probably have to do something different on 64 bit
architectures.

Finally, cast the varargs function to (lotsofargs).  Call the function
with the set of (void *) arguments you have put together.  Just pass
NULLs for the extra arguments.

The code would look something like this:

  void *args[N];
  int i = 0;
  lotsofargs func;

  for (arg in python_args) {
    if (PyString_Check(arg)) {
      if (i > N-1) raise TooManyArgsException;
      args[i++] = PyString_AsString(arg);
    } else if (PyInt_Check(arg)) {
      if (i > N-1) raise TooManyArgsException;
      args[i++] = (void *)PyInt_AsLong(arg);
    } else if (PyFloat_Check(arg)) {
      if (i > N-2) raise TooManyArgsException;
      split_double(PyFloat_AsDouble(arg), &args[i++], &args[i++]);
    } else
      raise TypeError;
  }
  func = (lotsofargs)real_func;
  func(non_varargs_arg, args[0], args[1], ..., args[N-1]);

I have done something like this before (without the double handling code
though), and it works fairly well.  It would be nice if there was some
standard `inverse varargs' function in standard C, but since there
isn't, you will probably have to use a hack like this.

James Henstridge.

Chris McDonough wrote:
> 
> Hi,
> 
> I'm working on a C extension that must accept arbitrary positional
> arguments of Python types string, int, and float and convert those to
> their C equivalents (int, char, float).
> 
> I'm sort of stuck, however, on creating a C routine which can accept and
> deconstruct into C equivalents the elements of the passed-in Python
> tuple that may contain arbitrary Python object types of int, float, and
> string arbitrarily positioned within the tuple.  It doesn't help that I
> don't know much C, either :-)
> 
> To explain a little better, I'd like to be able to call a function
> within a C extension with arbitrary arguments like so:
> 
> mymodule.function(1,"string",2.0)
> 
> or maybe
> 
> mymodule.function("string", "another string", 2.0, 1, "wow")
> 
> or maybe even
> 
> mymodule.function("lonely string")
> 
> I imagine the best thing to do here is to construct a wrapper for the
> function that determines the number of passed in arguments and their
> types.  The wrapper would then shove what it knows about the arguments
> into a dictionary of some kind which then would be shoved into the tuple
> that is passed to the C extension module.  But deconstructing that
> Python dictionary and retrieving and marshalling its contents within C
> is sort of beyond me.
> 
> Does anyone have an example of doing such a thing?
> 
> Many thanks in advance!
> 
> --
> Chris McDonough
> Digital Creations, Inc.
> Zope - http://www.zope.org



More information about the Python-list mailing list