Home Ask Login Register

Developers Planet

Your answer is one click away!

user595985 February 2016

Python lists and ctypes

Hi I am trying to call a c++ function that has the following signature

change_mountain_heights(mnt *mountains[],float32 heights[],int8 num_elements)

The idea being from python I can have a list of mountains and their heights, and I can call the c++ function to set their heights. The C++ Function takes 2 arrays. An array with the mountain names and another array with the mountain heights.

So in python I have 2 lists

  • mountains=[kilimanjaro,mount_saina,mount_elgon]
  • mnt_heights=[100.1,200,3331.56]
  • element_count=len(mountains)

I use ctypes and call the c-function as follows,

def change_heights(mountain_list,height_list,element_count):
    mountain_array=(ctypes.c_char_p * len(mountain_list))(*mountain_list)
    height_array=(ctypes.c_float * len(height_list))(*height_list)
    element_count_param=ctypes.c_int8(element_count)
    change_mountain_heights.argtypes=(ctypes.c_char_p,ctypes.c_float,ctypes.c_int8)
    _success=change_mountain_heights(mountain_array,height_array,element_count_param)

But it doesn't seem to work, I am not 100% sure if I am creating the array correctly as in both the size and making sure that the mountain strings are properly assigned to the array, similarly for the heights. Am I doing something wrong, or should I look elsewhere for the source of my issue?

Update

I think one of the issues stems from

    change_mountain_heights.argtypes=(ctypes.c_char_p,ctypes.c_float,ctypes.c_int8)

now the list of mountains is an array of strings, or in python a list of strings, how do I set the argtypes as a list of strings or at least an array of strings.

am I correct in assuming that ctypes.c_char_p which i use in argtypes does NOT allow me to pass into the function an array of strings?. What should i have in argtypes to

Answers


Neitsa February 2016

Here's a simple test in C (add 100.0 to each height) :

TMPDLL_API /* export */
int WINAPIV change_mountain_heights(char* mountains[], float heights[], int8_t num_elements)
{
    if (num_elements <= 0)
        return 0;

    if (!(mountains && heights))
        return 0;

    for (int8_t index = 0; index < num_elements; ++index) {
        char* mountain = mountains[index];
        printf("mountain: %s ; height: %f\n", mountain, heights[index]);

        // add 100.0
        heights[index] += 100.0f;
    }

    return 1;
}

The python code:

import ctypes
import os

current_dir = os.path.dirname(os.path.realpath(__file__))
dll_path = os.path.join(current_dir, "tmpDll.dll")

_tmp_dll = ctypes.CDLL(dll_path)

# int change_mountain_heights(char* mountains[], float heights[], int8_t num_elements)

_tmp_dll.change_mountain_heights.restype = ctypes.c_int  # default, not required
_tmp_dll.change_mountain_heights.argtypes = (
    ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_float),
    ctypes.c_int8)


def change_mountain_heights(mountains, mountain_heights):
    if len(mountains) != len(mountain_heights):
        raise ValueError()

    # pointer to strings
    string_pointers = (ctypes.c_char_p * len(mountains))(*mountains)
    # floats
    floats = (ctypes.c_float * len(mountain_heights))(*mountain_heights)
    # pointer to floats
    pfloat = ctypes.cast(floats, ctypes.POINTER(ctypes.c_float))

    # call C function
    result = _tmp_dll.change_mountain_heights(string_pointers, pfloat,
                                              len(mountain_heights))
    print("result: {}".format(result))
    if result == 1:
        # change passed list with new results
        del mountain_heights[:]
        mountain_heights.extend(list(floats))

    return result


def main():
    mountains = ["foo", "bar", "baz"]
    mountain_heights = [100.0, 200.0, 300.0]

    print("Before: {}".format(mountain_heights))
 

Post Status

Asked in February 2016
Viewed 2,069 times
Voted 8
Answered 1 times

Search




Leave an answer


Quote of the day: live life