1

I have a shared C library with a struct I would like to use in my python code

struct my_struct {
  char name[64];
};

so in python I recreate it with

class MyStruct(ctypes.Structure):
  _fields_ = [
    ("name", ctypes.c_char*64)
  ]

when I check the type of MyStruct.name i get 'str', whereas I expected 'c_char_Array_64'.

s=MyStruct()
print type(s.name) # <type 'str'>

So when I set the 'name' and try to use it, C sees it as blank.

s.name="Martin"
lib=ctypes.cdll.LoadLibrary('./mylib.so')
lib.my_func(s) # prints ''

where lib is the shared C library loaded with ctypes and my_func simply prints struct->name

void my_func(struct my_struct *s){
  printf("Hello %s\n", s->name);
}

I would like to know why ctypes.Structure converts the char-array to a string and how to use it in the case specified above.

Thank you

Update & Solution

Tnanks to @CristiFati for the help on debugging this problem. I have marked his answer as correct as it is in fact the answer to the question posted. In my case the problem was that the Structs were NOT of equal lengths in the Python and C program. So to whoever stumbles upon this question in the future, be very meticulous in checking that your Structs are in fact defined equally.

5
  • Can you show how my_func is defined in C and wrapped in ctypes/python? Commented Jan 8, 2019 at 10:23
  • It is already shown above Commented Jan 8, 2019 at 10:28
  • You need to define argtypes (and restype) for your function in Python (lib.my_func.argtypes = [ctypes.POINTER(MyStruct)], and call it: lib.my_func(ctypes.pointer(s))). Most of the ctypes failures are due to this reason. Check stackoverflow.com/questions/53182796/… (and tons of other questions) for more details. Commented Jan 8, 2019 at 10:31
  • Thank you @CristiFati, I will try this Commented Jan 8, 2019 at 10:34
  • Sorry @CristiFati, s->name is still blank. Have any additional approaches I can try? Commented Jan 8, 2019 at 10:46

1 Answer 1

5

You're doing something wrong, but without looking at the full code I can't say what. So I prepared a small example that works.
I'm also posting [Python.Docs]: ctypes - A foreign function library for Python as a reference.

dll00.c:

#include <stdio.h>
#include <stdlib.h>

#if defined(_WIN32)
#  define DLL00_EXPORT __declspec(dllexport)
#else
#  define DLL00_EXPORT
#endif


typedef struct Struct0_ {
    char name[64];
} Struct0;


DLL00_EXPORT void test(Struct0 *ps0)
{
    printf("Hello %s\n", ps0->name);
}

code00.py:

#!/usr/bin/env python

import ctypes as cts
import sys


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


CharArr64 = cts.c_char * 64

class Struct0(cts.Structure):
    _fields_ = (
        ("name", CharArr64),
    )


def main():
    dll = cts.CDLL(DLL_NAME)
    test = dll.test
    test.argtypes = (cts.POINTER(Struct0),)

    s0 = Struct0()
    s0.name = b"Martin"
    res = test(cts.pointer(s0))


if __name__ == "__main__":
    print(
        "Python {:s} {:03d}bit on {:s}\n".format(
            " ".join(elem.strip() for elem in sys.version.split("\n")),
            64 if sys.maxsize > 0x100000000 else 32,
            sys.platform,
        )
    )
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

Output:

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371> "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371>dir /b
code00.py
dll00.c

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371> cl /nologo /DDLL /MD dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
   Creating library dll00.lib and object dll00.exp

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" ./code00.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Hello Martin

Done.


(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371> rem Also run with Python 2.7 ... Not recommended.

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054089371> "e:\Work\Dev\VEnvs\py_064_02.07.15_test0\Scripts\python.exe" ./code00.py
Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32

Hello Martin

Done.

Might also want to check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer).

Sign up to request clarification or add additional context in comments.

7 Comments

My best guess is OP didn't set the argtypes for the function and so the struct is being passed by value and not by pointer. That is, the function is interpreting the string 'Martin\0\0' as pointer to MyStruct. eg. compare libc.printf(b'hello %sn', s) and libc.printf(b'hello %s\n', ctypes.byref(s)). The former prints garbage, whilst the latter works as it passes a pointer to s as the second argument (which just so happens to be the same as the pointer to s->name).
@Dunes: I specified in one comment how the function should be called, and according to the next comment, that was done. Then I really wanted to see if I could reproduce it, and I posted the answer.
@CristiFati I've tried your code and it works perfectly. Even tried porting it back to what my code looks like and it still works. I haven't posted 'the entire code' as it is a massive project I'm building on top of, and I thought what I posted would be enough to find the issue. But it is not. There must be somewhere else in the code that interferes with how the 'pass by reference works?' I appreciate the help
stackoverflow.com/questions/373419/…. In this scenario it has everything to do with how the function is called from Python. In the code there are 2 places: ctypes.POINTER and ctypes.pointer, those are the key, you probably missed one of them. Or it could be that you did the changes (in C sources), but forgot to rebuild.
SOLVED. @CristiFati. I found and issue and as you said "without seeing the entire code...". It turns out the structs were not equal lengths in python and C which created a mismatch in where the data were actually located. I will still accept your answer as it is in fact the answer to the question stated. Thank you for your help.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.