C Structure rebuild with ctypes

G

Georg

Hi All,

I need to use a library written in C. The routine "int func (int handle,
int *numVars, char ***varNames, int **varTypes)"

expects a complex object:

" ... Variable names are structured as an array of *numVars pointers, each
pointing to a char string containing a variable name, and *varNames is set
to point to the first element of the array. Variable types are stored into a
corresponding array of *numVars in elements, and *varTypes is set to point
to the first element of the array."

I tried using ctypes but nothing worked, e.g. "varNames = (c_char_p(c_char *
65) * NumberOfVariables)()"

Can anyboby help? How do I have to state the structure "array of pointers to
char string"? How is a pointer to the first element of such an array defined
using ctypes? How do I allocate enough space for the char the array points
to?

Best regards

Georg
 
M

Mark Tolonen

Georg said:
Hi All,

I need to use a library written in C. The routine "int func (int handle,
int *numVars, char ***varNames, int **varTypes)"

expects a complex object:

" ... Variable names are structured as an array of *numVars pointers, each
pointing to a char string containing a variable name, and *varNames is set
to point to the first element of the array. Variable types are stored into
a corresponding array of *numVars in elements, and *varTypes is set to
point to the first element of the array."

I tried using ctypes but nothing worked, e.g. "varNames = (c_char_p(c_char
* 65) * NumberOfVariables)()"

Can anyboby help? How do I have to state the structure "array of pointers
to char string"? How is a pointer to the first element of such an array
defined using ctypes? How do I allocate enough space for the char the
array points to?

Are you passing in these values, or are they being returned? To me the
depth of the pointer references implies numVars, varNames, and varTypes are
out parameters. I'll assume that for now. If they are in/out parameters let
me know.

I mocked up a DLL to test returning values of these types. I used VS2008
and compiled with "cl /LD func.c":

--- func.c -------------------------------
#include <stdlib.h>
#define FUNCDLL
#include "func.h"

static char* g_data[] = {"one","two","three"};
static int g_types[] = {1,2,3};

FUNCAPI int func (int handle, int *numVars, char ***varNames, int
**varTypes)
{
*numVars = _countof(g_data);
*varNames = g_data;
*varTypes = g_types;
return handle + 1;
}


--- func.h -------------------------------
#ifdef FUNCDLL
# define FUNCAPI __declspec(dllexport)
#else
# define FUNCAPI __declspec(dllimport)
#endif

FUNCAPI int func (int handle, int *numVars, char ***varNames, int
**varTypes);

--- func.py -------------------------------
import ctypes as c

# shortcuts for useful types
INT = c.c_int
PINT = c.POINTER(INT)
PPINT = c.POINTER(PINT)
PCHAR = c.c_char_p
PPCHAR = c.POINTER(PCHAR)
PPPCHAR = c.POINTER(PPCHAR)

# int func (int handle, int *numVars, char ***varNames, int **varTypes)
func = c.CDLL('func').func
func.restype = INT
func.argtypes = [INT,PINT,PPPCHAR,PPINT]

# allocate storage for the out parameters
numVars = INT()
varNames = PPCHAR()
varTypes = PINT()

print func(5,c.byref(numVars),c.byref(varNames),c.byref(varTypes))

# numVars contains size of returned arrays. Recast to access.
varNamesArray = c.cast(varNames,c.POINTER(PCHAR * numVars.value))
varTypesArray = c.cast(varTypes,c.POINTER(INT * numVars.value))
for value in varNamesArray.contents:
print value
for value in varTypesArray.contents:
print value

--- output -------------------------------
6
one
two
three
1
2
3

Hope this helps,
-Mark
 
G

Georg

Hi Mark,

many thanks for your help. I tried your code in my program and it worked.

I would like to understand what the code is doing and I have some questions
to it.
Are you passing in these values, or are they being returned? To me the
depth of the pointer references implies numVars, varNames, and varTypes
are out parameters. I'll assume that for now. If they are in/out
parameters let me know.

Your exactly right: the parameters numVars, varNames and VarTypes are out
paramters.
I mocked up a DLL to test returning values of these types. I used VS2008
and compiled with "cl /LD func.c":

--- func.c -------------------------------
#include <stdlib.h>
#define FUNCDLL
#include "func.h"

static char* g_data[] = {"one","two","three"};
static int g_types[] = {1,2,3};

FUNCAPI int func (int handle, int *numVars, char ***varNames, int
**varTypes)
{
*numVars = _countof(g_data);
*varNames = g_data;
*varTypes = g_types;
return handle + 1;
}

What does the signature FUNCAPI do in this context?

--- func.h -------------------------------
#ifdef FUNCDLL
# define FUNCAPI __declspec(dllexport)
#else
# define FUNCAPI __declspec(dllimport)
#endif

FUNCAPI int func (int handle, int *numVars, char ***varNames, int
**varTypes);

Do I need to wrap the compiled DLL file this way? I can load the DLL with
the CDLL method, make calls to simple functions, e.g. no parameters,
returning stirng and get the right values.


I added all the code from your func.py module to the code I already had.
And -- it works fine and delivers the expected results.
--- func.py ------------------------------- .... cut ...

# int func (int handle, int *numVars, char ***varNames, int **varTypes)
func = c.CDLL('func').func
func.restype = INT
func.argtypes = [INT,PINT,PPPCHAR,PPINT]

I added this part to my program.
# allocate storage for the out parameters
numVars = INT()
varNames = PPCHAR()
varTypes = PINT()

I added this part also.
print func(5,c.byref(numVars),c.byref(varNames),c.byref(varTypes))

I called the library routine.
# numVars contains size of returned arrays. Recast to access.
varNamesArray = c.cast(varNames,c.POINTER(PCHAR * numVars.value))
varTypesArray = c.cast(varTypes,c.POINTER(INT * numVars.value))

What does this cast? How do I know how I have to cast the objects returned
from the library function?

What kind of objects do I get? I learned that the values of objects created
by the ctypes module are accessed using object.value?

Best regards

Georg
 
M

Mark Tolonen

Georg said:
Hi Mark,

many thanks for your help. I tried your code in my program and it worked.

I would like to understand what the code is doing and I have some
questions to it.
Are you passing in these values, or are they being returned? To me the
depth of the pointer references implies numVars, varNames, and varTypes
are out parameters. I'll assume that for now. If they are in/out
parameters let me know.

Your exactly right: the parameters numVars, varNames and VarTypes are out
paramters.
I mocked up a DLL to test returning values of these types. I used VS2008
and compiled with "cl /LD func.c":

--- func.c -------------------------------
#include <stdlib.h>
#define FUNCDLL
#include "func.h"

static char* g_data[] = {"one","two","three"};
static int g_types[] = {1,2,3};

FUNCAPI int func (int handle, int *numVars, char ***varNames, int
**varTypes)
{
*numVars = _countof(g_data);
*varNames = g_data;
*varTypes = g_types;
return handle + 1;
}

What does the signature FUNCAPI do in this context?

In Microsoft's compiler, to export functions from a DLL, one way is to mark
functions with __declspec(dllexport). To import functions from a DLL, the
functions should be marked with __declspec(dllimport). FUNCAPI is declared
in func.h below to be one of these definitions. In the actual DLL source,
FUNCDLL is defined before including the header to declare all the
FUNCAPI-tagged functions in the header as exported.

In an executable file that uses the DLL, it can include func.h without
defining FUNCDLL, and all the FUNCAPI-tagged functions in the header will be
declared imported.

It was overkill for this example, but I had code skeletons for DLLs already.
The example could be simplified by replacing FUNCAPI in func.cpp with
__declspec(dllimport) and not using func.h at all.
Do I need to wrap the compiled DLL file this way? I can load the DLL with
the CDLL method, make calls to simple functions, e.g. no parameters,
returning stirng and get the right values.

No, if the DLL is exporting functions correctly this method doesn't have to
be used. As I said above it was a code template I already had. IIRC, the
Microsoft code wizard to generate a DLL uses this method. I didn't have
your DLL to test with, so I had to make my own and I wanted you to see the
assumptions I made about its implementation, in case they were wrong :^)
I added all the code from your func.py module to the code I already had.
And -- it works fine and delivers the expected results.
--- func.py ------------------------------- ... cut ...

# int func (int handle, int *numVars, char ***varNames, int **varTypes)
func = c.CDLL('func').func
func.restype = INT
func.argtypes = [INT,PINT,PPPCHAR,PPINT]

I added this part to my program.
# allocate storage for the out parameters
numVars = INT()
varNames = PPCHAR()
varTypes = PINT()

I added this part also.
print func(5,c.byref(numVars),c.byref(varNames),c.byref(varTypes))

I called the library routine.
# numVars contains size of returned arrays. Recast to access.
varNamesArray = c.cast(varNames,c.POINTER(PCHAR * numVars.value))
varTypesArray = c.cast(varTypes,c.POINTER(INT * numVars.value))

What does this cast? How do I know how I have to cast the objects returned
from the library function?

The library is returning a pointer to an array of some size you didn't know
in advance. So, for example, we passed varTypes as the address of a pointer
to int, and a pointer to int was returned. Since varTypes was declared as a
pointer to a single int, I recast it to a pointer to "numVars" ints. This
allowed me to use indexing syntax ([n]) to access the other elements of the
returned array.
What kind of objects do I get? I learned that the values of objects
created by the ctypes module are accessed using object.value?

numVars is the name of an INT() object, not a Python int. numVars.value
returns the Python int that the INT represents. An example might be
helpful:
Traceback (most recent call last):
File said:
>>> y=(ctypes.c_int * x.value)() # It works with Python values.
>>> y
said:
>>> y[0] 0
>>> y[1]
0

Hope that helps,
Mark
 
G

Georg

Hi Mark,

many thanks for your valuable help.

One last question: You created an object varNamesArray as an ctypes array.
This object has a method "contents". How do I find out what other methods
this objects has? For instance a method to retrieve the size of the array?
Is this documented somewhere?

Best regards

Georg
 
M

Mark Tolonen

Georg said:
Hi Mark,

many thanks for your valuable help.


One last question: You created an object varNamesArray as an ctypes array.
This object has a method "contents". How do I find out what other methods
this objects has? For instance a method to retrieve the size of the array?
Is this documented somewhere?

The "contents" property is how you dereference a ctypes pointer. See
"16.15. ctypes" and specifically "16.15.1.14. Pointers" in the Python 2.6.4
documentation. "16.15.1.13. Arrays" documents that arrays support len().

5

You can use "dir(p)" or "help(p)" to find out information about methods and
attributes of an object. Both show that 'p' has a contents attribute and
'a' supports the __len__ method.

-Mark
 
G

Georg

Hi Mark,
Are you passing in these values, or are they being returned? To me the
depth of the pointer references implies numVars, varNames, and varTypes
are out parameters. I'll assume that for now. If they are in/out
parameters let me know.

If these parameters were in parameters. What would I have to do different?

Best regards

Georg
 
M

Mark Tolonen

Georg said:
Hi Mark,


If these parameters were in parameters. What would I have to do different?

If you are passing in the values, you can remove a level of * referencing,
since the parameters aren't being modified. Note the syntax to create an
array: (type * length)(initializers...):

--------------- func.c --------------------
#include <stdio.h>

__declspec(dllexport) void func(int numVars, char **varNames, int *varTypes)
{
int i;
printf("numVars = %d\n",numVars);
for(i = 0; i < numVars; i++)
printf("%d: %s\n",varTypes,varNames);
}

--------------- func.py ------------------
import ctypes as c

# int func (int numVars, char **varNames, int *varTypes)

INT = c.c_int
PINT = c.POINTER(INT)
PCHAR = c.c_char_p
PPCHAR = c.POINTER(PCHAR)

func = c.CDLL('func').func
func.restype = None
func.argtypes = [INT,PPCHAR,PINT]

numVars = 3
varNames = (PCHAR * numVars)('abc','def','ghi')
varTypes = (INT * numVars)(1,2,3)

func(numVars,varNames,varTypes)

--------------- OUTPUT ---------------
numVars = 3
1: abc
2: def
3: ghi
 
G

Georg

Hi Mark,

many thanks for your hints.
--------------- func.py ------------------
import ctypes as c

# int func (int numVars, char **varNames, int *varTypes)

INT = c.c_int
PINT = c.POINTER(INT)
PCHAR = c.c_char_p
PPCHAR = c.POINTER(PCHAR)

func = c.CDLL('func').func
func.restype = None
func.argtypes = [INT,PPCHAR,PINT]

numVars = 3
varNames = (PCHAR * numVars)('abc','def','ghi')
varTypes = (INT * numVars)(1,2,3)

func(numVars,varNames,varTypes)

The function to call has the following signiture:

int SetVarLabel(int handle, const char *varName, const char *varLabel)

I did as you suggested by doing the following:

INT = c_int
PCHAR = c_char_p

libc.argtypes = [INT,PCHAR,PCHAR]

vn = create_string_buffer("var1")
vl = create_string_buffer("Label")

print "vn: ", vn.value
print "vl: ", vl.value

returnCode = libc.SetVarLabel(h,byref(vn), byref(vl))
print "Return Code: ", returnCode

The return code is always "variable not found". Thus my call to the C
library is not working.

What do I do wrong?

Best regards

Georg
 

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

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top