3

I am accessing an API and can't get the data returned. The two float pointers will point to an array of data. I must assume the API is working properly. A different function call provides a the length of the data I am retrieving. This values is length down below when attempted.

C Header for Function

    int function(int, float * data1, float * data2)

ctypes setup

    dll.function.argtypes = (c_int, POINTER(c_float), POINTER(c_float))
    dll.function.restypes = c_int

Failed Attempt 1:

    x = c_float()
    y = c_float()
    status = dll.function(1, byref(x), byref(y))

Program crashes OR Access violation writing.

Failed Attempt 2:

    x = POINTER(c_float)()
    y = POINTER(c_float)()
    status = dll.function(1, x, y)

Null Pointer Error

Failed Attempt 3:

    dll.function.argtypes = (c_int, c_void_p, c_void_p)
    x = c_void_p()
    y = c_void_p()
    status = dll.function(1, x, y)

Null Pointer Error

Failed Attempt 4:

    array = c_float * length
    x = array()
    y = array()
    status = dll.function(1, byref(x), byref(y))

Program crashes

Failed Attempt 5:

    array = c_float * length
    x = POINTER(array)()
    y = POINTER(array)()
    status = dll.function(1, x, y)

Null Pointer Error OR ArgumentError: expected LP_c_float instance instead of LP_c_float_Array_[length]

Failed Attempt 6:

    x = (c_float*length)()
    y = (c_float*length)()
    a = cast(x, POINTER(c_float))
    b = cast(y, POINTER(c_float))
    status = dll.function(1, a, b)

Program crashes

What am I missing and why?

I believe the argtypes are correct. I am attempting to meet them properly, but there continues to be an issues. Do I need to "malloc" the memory somehow? (I'm sure I need to free after I get the data).

This is on Windows 7 with Python 2.7 32-bit.

I have looked through other similar issues and am not finding a solution. I am wondering if, at this point, I can blame the API for this issue.

4
  • It is recommended to use cffi instead of ctypes. cffi is much more convenient.cffi.readthedocs.io/en/latest/overview.html A while ago I was also using ctypes, but people recommended to switch. And they were right, I would never tough ctypes again. :) Commented Apr 26, 2018 at 13:13
  • Examples on how to use pointers can be found at cffi.readthedocs.io/en/latest/… Commented Apr 26, 2018 at 13:15
  • Both char* have the same length? They are allocated inside function or should they be allocated by the caller (Python)? Commented Apr 26, 2018 at 16:23
  • Both float* are pointers to arrays. They will need to be allocated by the caller. Commented Apr 26, 2018 at 17:11

2 Answers 2

3

Dealing with pointers and arrays is explained in [Python.Docs]: ctypes - Type conversions.

I prepared a dummy example for you.

main00.c:

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


static int kSize = 5;


DECLSPEC_DLLEXPORT int size() {
    return kSize;
}


DECLSPEC_DLLEXPORT int function(int dummy, float *data1, float *data2) {
    for (int i = 0; i < kSize; i++) {
        data1[i] = dummy * i;
        data2[i] = -dummy * (i + 1);
    }
    return 0;
}

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct


c_float_p = ct.POINTER(ct.c_float)


def main(*argv):
    dll = ct.CDLL("./dll00.so")

    size = dll.size
    size.argtypes = []
    size.restype = ct.c_int


    function = dll.function
    function.argtypes = [ct.c_int, c_float_p, c_float_p]
    function.restype = ct.c_int

    sz = size()
    print(sz)

    data1 = (ct.c_float * sz)()
    data2 = (ct.c_float * sz)()

    res = function(1, ct.cast(data1, c_float_p), ct.cast(data2, c_float_p))
    for i in range(sz):
        print(data1[i], data2[i])


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

Notes:

  • The C part tries to mimic what your .dll does (or at least what I understood):
    • size - gets the arrays sizes
    • function - populates the arrays (till their size - assuming that they were properly allocated by the caller)
  • Python part is straightforward:
    • Load the .dll
    • Define argtypes and restype (in your code it's restype's) for the 2 functions (for size_func not necessary)
    • Get the lengths
    • Initialize the arrays
    • Pass them to function_func using ctypes.cast

Output (on Lnx, as building the C code is much simpler, but works on Win as well):

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> gcc -shared -o dll00.so main00.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> python3 code00.py
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux

5
0.0 -1.0
1.0 -2.0
2.0 -3.0
3.0 -4.0
4.0 -5.0
Sign up to request clarification or add additional context in comments.

2 Comments

Did this answer your question?
Just accepted. I got pulled away to something else for a week. Thank you.
0

It really depends on what you are doing with these float pointers.

If you are trying to traverse it, i.e.

for(int i = 0; i < size; i++)
    printf("%f\n%, data1[i])

then for sure this is problematic as no array was allocated. You simply passed a pointer pointing to a float. That is all.

You need to first allocate that memory. To this end Attempt 4 looks like the more promising, but I suspect you have a problem inside your C function leading to the crash.

Difficult to say without seeing the implementation of that function.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.