Pass array from DLL to Python

Started by
7 comments, last by cowcow 6 years ago

I'm having trouble getting an array filled from a DLL.

Here's my DLL code:


	DLLEXPORT void get_triangles(int *p)
{
    vector<int> ints;
    ints.push_back(123);
    ints.push_back(456);
	    p = &ints[0];
}
	

.... and here's the Python code:


	from ctypes import cdll
	mydll = cdll.LoadLibrary('mc_dll.dll')
	arr = []
	mydll.get_triangles(arr)
	

Advertisement

That vector is allocated on the stack. As soon as your function returns it will no longer exist.

In addition, your assignment of p will not do what you expect.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

The "proper" way to do it would be something like this, if you had read the documentation, specifically section 16.16.2.9 about arrays and pointers:

Quote

The recommended way to create concrete array types is by multiplying any ctypes data type with a positive integer.

 

So let's do that:


DLLEXPORT void get_triangles(int* p)
{
    p[0] = 123;
    p[1] = 456;
}

import ctypes as ct

IntArray2 = ct.c_int * 2  # define new type that is an array of 2 ints
arr = IntArray2()         # instantiate this new type

lib = ct.CDLL("./mything.dll")
lib.get_triangles(arr)    # pass it to your get_triangles function
print(arr[0])             # prints 123
print(arr[1])             # prints 456

 

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

OK thank you very much; that seemed to have helped.

How would I set up for and call the function from Python:


	DLLEXPORT int print_normal_to_file(float x_grid_min, float x_grid_max, float y_grid_min,
                            float y_grid_max, float z_grid_min, float z_grid_max,
                            size_t x_res, size_t y_res, size_t z_res,
                            char *filename)
	

See here on examples on how to handle strings:
https://docs.python.org/3/library/ctypes.html#return-types

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

I got it all working. Thank you for the information.

I am now able to call from Python a function that I wrote which generates the surface normal of a quaternion Julia set using Marching Cubes.

 

My final code is...


#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT void get_normal(float x_grid_min, float x_grid_max, float y_grid_min, 
                            float y_grid_max, float z_grid_min, float z_grid_max, 
                            unsigned int x_res, unsigned int y_res, unsigned int z_res,
                            float *output)
{
// ... stuff
}
	

and...


def get_normal_from_mc(src_x_grid_min, src_x_grid_max,
                       src_y_grid_min, src_y_grid_max,
                       src_z_grid_min, src_z_grid_max,
                       src_x_res, src_y_res, src_z_res):
	float3 = ct.c_float * 3
    int1 = ct.c_uint
    float1 = ct.c_float
	x_grid_min = float1(src_x_grid_min)
    x_grid_max = float1(src_x_grid_max)
    y_grid_min = float1(src_y_grid_min)
    y_grid_max = float1(src_y_grid_max)
    z_grid_min = float1(src_z_grid_min)
    z_grid_max = float1(src_z_grid_max)
	x_res = int1(src_x_res)
    y_res = int1(src_y_res)
    z_res = int1(src_z_res)
	output_arr = float3()
	lib = ct.CDLL("mc_dll.dll")
	lib.get_normal(x_grid_min, x_grid_max,
                      y_grid_min, y_grid_max,
                      z_grid_min, z_grid_max,
                      x_res, y_res, z_res,
                      output_arr)
	ret = V3(output_arr[0], output_arr[1], output_arr[2]) 
 
 return ret
	

A lot of those conversions are unnecessary and you should probably load the DLL "outside" of the function that's calling it. Another thing I noticed is this section:

Quote

 

16.16.1.8. Return types


By default functions are assumed to return the C int type. Other return types can be specified by setting the restype attribute of the function object.

 

Your function's return value is void so you need to set it as such.

Here's how I'd do it:


import ctypes as ct

# Load DLL and initialize all types once here
float3 = c_float * 3
mc_dll = ct.CDLL("mc_dll.dll")
mc_dll.get_normal.argtypes = [c_float, c_float, c_float, c_float, c_float, c_float, c_uint, c_uint, c_uint, float3]
mc_dll.get_normal.restype = None  # returns void

def get_normal(x_grid_min, x_grid_max,
               y_grid_min, y_grid_max,
               z_grid_min, z_grid_max,
               x_res, y_res, z_res):
	output_arr = float3()
	mc_dll.get_normal(x_grid_min, x_grid_max,
                      y_grid_min, y_grid_max,
                      z_grid_min, z_grid_max,
                      x_res, y_res, z_res,
                      output_arr)
	return V3(output_arr[0], output_arr[1], output_arr[2])

Then, if you were to save this to "mc.py" you could use it as:


import mc

normal = mc.get_normal(...)

 

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

Hell yes, that's exactly what I wanted to do! Thanks again.

This topic is closed to new replies.

Advertisement