3

Currently I'm learning about C types. My goal is to generate an numpy array A in python from 0 to 4*pi in 500 steps. That array is passed to C code which calculates the tangent of those values. The C code also passes those values back to an numpy array B in python.

Yesterday I tried simply to convert one value from python to C and (after some help) succeeded. Today I try to pass a whole array, not a value.

I think it's an good idea to add another function to the C library to process the array. The new function should in a loop pass each value of A to the function tan1() and store that value in array B.

I have two issues:

  • writing the function that processes the numpy array A
  • Passing the numpy array between python and C code.

I read the following info:

Helpful, but I still don't know how to solve my problem.

C code (Only the piece that seems relevant):

double tan1(f) double f;
{
    return sin1(f)/cos1(f); 
}


void loop(double A, int n);
{
    double *B;
    B = (double*) malloc(n * sizeof(double));
    for(i=0; i<= n, i++)
    {
        B[i] = tan1(A[i])
    }
}

Python code:

import numpy as np
import ctypes


A = np.array(np.linspace(0,4*np.pi,500), dtype=np.float64)

testlib = ctypes.CDLL('./testlib.so')
testlib.loop.argtypes = ctypes.c_double,
testlib.loop.restype = ctypes.c_double


#print(testlib.tan1(3))
    

I'm aware that ctypes.c_double is wrong in this context, but that is what I had in the 1 value version and don't know yet for what to substitute.

Could I please get some feedback on how to achieve this goal?

4
  • 1
    You're not working with NumPy from C (only with basic types), so I see no reason to use it in Python either. It just brings an extra complexity layer to your goal (learn CTypes). Commented Oct 22, 2020 at 9:31
  • @CristiFati it's part of the assignment to do it with numpy. You're right that I'm not working with Numpy yet, but I want to pass A to the C library. Commented Oct 22, 2020 at 9:34
  • Does this answer your question? How to use NumPy array with ctypes? Commented Oct 22, 2020 at 12:28
  • The dupe answers your question (see the title of your question) but doesn’t solve your problem, because your first problem is that your c code doesn’t compile and doesn’t make much sense, as it has no visible effect whatsoever ( if Introduced memory leak is not taken into account) Commented Oct 22, 2020 at 12:35

1 Answer 1

4

You need to return the dynamically allocated memory, e.g. change your C code to something like:

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

double tan1(double f) {
    return sin(f)/cos(f);
}

double *loop(double *arr, int n) {
    double *b = malloc(n * sizeof(double));
    for(int i = 0; i < n; i++) {
        b[i] = tan(arr[i]);
    }
    return b;
}

void freeArray(double *b) {
    free(b);
}

On the Python side you have to declare parameter and return types. As mentioned by others in comments, you should also free dynamically allocated memory. Note that on the C side, arrays always decay into pointers. Therefore, you need an additional parameter which tells you the number of elements in the array.

Also if you return a pointer to double to the Python page, you must specify the size of the array. With np.frombuffer you can work with the data without making a copy of it.

import numpy as np
from ctypes import *

testlib = ctypes.CDLL('./testlib.so')

n = 500
dtype = np.float64
input_array = np.array(np.linspace(0, 4 * np.pi, n), dtype=dtype)
input_ptr = input_array.ctypes.data_as(POINTER(c_double))

testlib.loop.argtypes = (POINTER(c_double), c_int)
testlib.loop.restype = POINTER(c_double * n)
testlib.freeArray.argtypes = POINTER(c_double * n),

result_ptr = testlib.loop(input_ptr, n)
result_array = np.frombuffer(result_ptr.contents)

# ...do some processing
for value in result_array:
    print(value)

# free buffer
testlib.freeArray(result_ptr)
Sign up to request clarification or add additional context in comments.

2 Comments

testlib.freeArray.argtypes = POINTER(c_double * n), is incorrect (at least it doesn't reflect C).
result_ptr (allocated on C side via malloc) is a pointer to 500 doubles. The very same pointer is passed to freeArray. Arrays decay into pointers on C anyway. So the type is implicitly converted from an array of 500 doubles to a pointer to double, the values of the function argument is the address of the first double value.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.