Help creating extension for C function

J

Java and Swing

I need to write an extension for a C function so that I can call it
from python.

C code (myapp.c)
======
typedef unsigned long MY_LONG;

char *DoStuff(char *input, MY_LONG *x) { ... }

so you pass in a string and an array of MY_LONGS...such as

MY_LONGS vals[10] = {....};
char *input = "this is my input";
char *result;

result = DoStuff(input, vals);

....I need to create an extension so that I can call this from Python
such as...
import myapp
vals = [1,2,3,4,5,6,7,8,9,10]
input = "this is my input"
result = myapp.DoStuff(input, vals)
print result

ideally the result would be a String, vals would be a list and input
would be a string.

I was hoping to follow along with how to write an extension, as shown
here http://docs.python.org/ext/contents.html.

....but a better example would help.

Thanks.
 
F

Fredrik Lundh

Java and Swing said:
I need to write an extension for a C function so that I can call it
from python.

C code (myapp.c)
======
typedef unsigned long MY_LONG;

char *DoStuff(char *input, MY_LONG *x) { ... }

so you pass in a string and an array of MY_LONGS...such as

MY_LONGS vals[10] = {....};
char *input = "this is my input";
char *result;

result = DoStuff(input, vals);

...I need to create an extension so that I can call this from Python
such as...
import myapp
vals = [1,2,3,4,5,6,7,8,9,10]
input = "this is my input"
result = myapp.DoStuff(input, vals)
print result

ideally the result would be a String, vals would be a list and input
would be a string.

$ more module.c

/* $Id$ */
/* a module */

#include "Python.h"

static long*
get_long_array(PyObject* data, int* data_size)
{
int i, size;
long* out;
PyObject* seq;

seq = PySequence_Fast(data, "expected a sequence");
if (!seq)
return NULL;

size = PySequence_Size(seq);
if (size < 0)
return NULL;

if (data_size)
*data_size = size;

out = (long*) PyMem_Malloc(size * sizeof(long));
if (!out) {
Py_DECREF(seq);
PyErr_NoMemory();
return NULL;
}

for (i = 0; i < size; i++)
out = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i));

Py_DECREF(seq);

if (PyErr_Occurred()) {
PyMem_Free(out);
out = NULL;
}

return out;
}

static PyObject*
do_stuff(PyObject* self, PyObject* args)
{
int i;
long* vals;
int vals_size;

char* my_result;

char* input;
PyObject* vals_in;
if (!PyArg_ParseTuple(args, "sO:do_stuff", &input, &vals_in))
return NULL;

vals = get_long_array(vals_in, &vals_size);
if (!vals)
return NULL;

if (vals_size != 10) {
PyErr_SetString(PyExc_ValueError, "expected 10 values");
PyMem_Free(vals);
return NULL;
}

/* do stuff */
printf("input = %s\n", input);
for (i = 0; i < 10; i++)
printf("vals[%d] = %ld\n", i, vals);
my_result = "result";
/* done */

PyMem_Free(vals);

return PyString_FromString(my_result);
}

static PyMethodDef functions[] = {
{"do_stuff", do_stuff, METH_VARARGS},
{NULL, NULL}
};

PyMODINIT_FUNC initmodule(void)
{
Py_InitModule4(
"module", functions, "my module", NULL, PYTHON_API_VERSION
);
}

$ more setup.py

# $Id$
# a setup file

from distutils.core import setup, Extension

setup(
name="module",
ext_modules = [Extension("module", ["module.c"])]
)

$ python setup.py build_ext -i
....

$ python
import module
vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
input = "this is some input"
result = module.do_stuff(input, vals)
input = this is some input
vals[0] = 1
vals[1] = 2
vals[2] = 3
vals[3] = 4
vals[4] = 5
vals[5] = 6
vals[6] = 7
vals[7] = 8
vals[8] = 9
vals[9] = 10'result'

</F>
 
J

Java and Swing

also, I noticed you did python build-ext ....does that mean for any
computer I want to run myapp on, I need to build the extension on it as
well? Or can I build it once and just copy the files to another
computer that has python already installed? If I just copy files,
which files and where would they go?

Thanks!
 
F

Fredrik Lundh

Java said:
So is "module.c" a new C file or do I add it to my existing, myapp.c?

it's a complete module. if you want it to do something other than printing
the arguments, replace the "do stuff" section with your own code. if you
want to call it something else, rename it. if you want to change the API,
change it. it's not that large; you should be able to figure out what it does
and how it does it in no time at all.

</F>
 
J

Java and Swing

Fredrik,
I now have this.

myapp.c
--------
#include <string.h>
#include <stdlib.h>
#include "Python.h"

int doStuff(const char *input, const char *d) { ... }

static PyObject *wrap_doStuff(PyObject *self, PyObject *args) {
// get the arguments from Python
int result;
char *input = 0;
char *d = 0;
int ok = PyArg_ParseTuple(args, "ss", &input, &d);
if (!ok) return 0;

// make the function call
result = doStfuff(input, d);

// return the result
return PyBuildValue("i", result);
}

static PyMethodDef functions[] =
{
{"PyDoStuff", wrap_doStuff, METH_VARARGS, "some documentation"},
{NULL, NULL}
};

extern PyMODINIT_FUNC initDLLTester(void)
{
Py_InitModule4(
"DLLTester", functions, "my doStfuff function", NULL,
PYTHON_API_VERSION
);

}

....when I try to compile in Visual C++ 6, I get

Linking...
Creating library Release/DLLTester.lib and object
Release/DLLTester.exp
test.obj : error LNK2001: unresolved external symbol _PyBuildValue
Release/DLLTester.dll : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

Any ideas what's happening here?

DLLTester.dll - 2 error(s), 0 warning(s)
 
J

Java and Swing

...ok I modified wrap_doStuff, so it just returns 0 instead of return
PyBuildValue....this fixed the problems I had with compiling.

however, when I try using it in Python..I get a SystemError: error
return without exception set.

Anyhow, I need PyBuildValue to work. One other note, the VC++ 6
compiler gives two warnings, one of which is...

"warning C4013: 'PyBuildValue' undefined; assuming extern returning
int"

thanks.
Fredrik,
I now have this.

myapp.c
--------
#include <string.h>
#include <stdlib.h>
#include "Python.h"

int doStuff(const char *input, const char *d) { ... }

static PyObject *wrap_doStuff(PyObject *self, PyObject *args) {
// get the arguments from Python
int result;
char *input = 0;
char *d = 0;
int ok = PyArg_ParseTuple(args, "ss", &input, &d);
if (!ok) return 0;

// make the function call
result = doStfuff(input, d);

// return the result
return PyBuildValue("i", result);
}

static PyMethodDef functions[] =
{
{"PyDoStuff", wrap_doStuff, METH_VARARGS, "some documentation"},
{NULL, NULL}
};

extern PyMODINIT_FUNC initDLLTester(void)
{
Py_InitModule4(
"DLLTester", functions, "my doStfuff function", NULL,
PYTHON_API_VERSION
);

}

...when I try to compile in Visual C++ 6, I get

Linking...
Creating library Release/DLLTester.lib and object
Release/DLLTester.exp
test.obj : error LNK2001: unresolved external symbol _PyBuildValue
Release/DLLTester.dll : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

Any ideas what's happening here?

DLLTester.dll - 2 error(s), 0 warning(s)

Fredrik said:
it's a complete module. if you want it to do something other than printing
the arguments, replace the "do stuff" section with your own code. if you
want to call it something else, rename it. if you want to change the API,
change it. it's not that large; you should be able to figure out what it does
and how it does it in no time at all.

</F>
 
J

Java and Swing

quick question...how about returning an array of longs? ..or in my
case I have a pointer to an array of longs.

Thanks
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top