[plt-scheme] Reusing the argv parameter in C extension primitives

From: Daniel Silva (daniel.silva at gmail.com)
Date: Thu Nov 25 05:00:27 EST 2004

Hi,

To allow this Scheme code:

(spy-apply some-py-function pyobj1 pyobj2 ...)

I had a C primitive that looked like this:

Scheme_Object* spy_apply(const char* name, int argc, Scheme_Object* argv[])
{
  Scheme_Object* scheme_pyfn = argv[0];
  PyObject* fnobj = PY(scheme_pyfn);
  PyObject* args = PyTuple_New(argc - 1);
  PyObject* result;
  int i;
  for ( i = 1; i < argc; i++ )
    {
    PyObject* arg = PY(argv[i]);
    PyTuple_SetItem(args, i - 1, arg);
    }
  result = PyObject_CallObject(fnobj, args);
  return SCM( result );
}

where the two functions SCM(py) and PY(scm) wrap/unwrap PyObjects
around SCHEME_CPTRP objects.

Some python function objects, though, are actually wrappers around
Scheme procedures, and PyObject_CallObject will call this function for
them:

PyObject* PySchemeFunction_Call(PyObject* self, PyObject* argsTuple,
PyObject* ignored)
{
  PySchemeFunctionObject* fn = (PySchemeFunctionObject*) self;
  Scheme_Object* argv[1024];
  Scheme_Object* result;

  int argc = PyTuple_GET_SIZE(argsTuple);

  int i;
  for ( i = 0; i < argc; i++ )
    {
    PyObject* arg = PyTuple_GET_ITEM(argsTuple, i);
    argv[i] = SCM(arg);
    }
  result = _scheme_apply(fn->proc, argc, argv);
  return PY(result);
}


I thought that translating the argv array into a Python tuple and then
back into another argv array was sort of wasteful, so I tried to edit
spy_apply:

Scheme_Object* spy_apply(const char* name, int argc, Scheme_Object* argv[])
{
  Scheme_Object* scheme_pyfn = argv[0];
  PyObject* fnobj = PY(scheme_pyfn);

  if (fnobj->ob_type == &PySchemeFunction_Type)
   return _scheme_apply(((PySchemeFunctionObject*)fnobj)->proc,
                                       argc - 1, argv + 1);

   ....
}

But if the Scheme procedure used call/cc then my code would end up
jumping to random places.  Should I always make a copy of argv instead
of reusing it in another scheme_apply?  If so, can I keep a single
static argv array for that purpose, like this?

static Scheme_Object* spy_apply_argv[1024];

Scheme_Object* scheme_initialize(Scheme_Env* ns)
{
  scheme_register_extension_global(&spy_apply_argv, sizeof(spy_apply_argv));
  ...
}

Scheme_Object* spy_apply(const char* name, int argc, Scheme_Object* argv[])
{
  Scheme_Object* scheme_pyfn = argv[0];
  PyObject* fnobj = PY(scheme_pyfn);

  if (fnobj->ob_type == &PySchemeFunction_Type)
   {
     Scheme_Object* res;
     int i = 0, argcc = argc - 1;
      // maybe use memcpy here instead
     for ( ; i < argcc; i++ )
       spy_apply_argv[i] = argv[i + 1];
   return _scheme_apply(((PySchemeFunctionObject*)fnobj)->proc,
                                       argcc, spy_apply_argv);
   }
 ...
}

Daniel


Posted on the users mailing list.