Вызов функции C в Python через ctypes, но функция возвращает неверное значение - PullRequest
1 голос
/ 29 октября 2019

Я написал простую функцию в C, которая поднимает заданное число до заданной степени. Функция возвращает правильное значение, когда я вызываю ее в C, но когда я вызываю ее в Python, она возвращает другое неправильное значение.

Я создал общий файл с помощью команды: $ gcc -fPIC -shared -o test.so test.c

Я пробовал разные конфигурации функции C, некоторые из которых возвращают ожидаемое значение, а некоторые нет. Например, когда моя функция представляет собой простой квадрат с использованием return x*x, без цикла for, она возвращает правильное значение в Python.

Я надеюсь, что в конечном итоге смогу вызвать функцию C в Python, которая будетвернуть двумерный массив C.

#include <stdio.h>

float power(float x, int exponent)
{
    float val = x;
    for(int i=1; i<exponent; i++){
        val = val*x;
    }
    return val;
}
from ctypes import *

so_file = '/Users/.../test.so'
functions = CDLL(so_file)

functions.power.argtype = [c_float, c_int]
functions.power.restype = c_float

print(functions.power(5,3))

Я получаю ожидаемый результат 125.0, когда я вызываю функцию в C, но когда я вызываю функцию в python, она возвращает значение 0.0.

Я впервые использую ctypes. Я допустил явную ошибку, которая вызывает неправильный расчет функции?

1 Ответ

1 голос
/ 29 октября 2019

Листинг [Python 3.Docs]: ctypes - библиотека сторонних функций для Python .

Для того, чтобы все было правильно преобразовано ( Python <=> C ) при вызове функции (находящейся в .dll ( .so )) необходимо указать 2 вещи (оставив x86 соглашение о вызовах ( Win ) в стороне):

  1. Типы аргументов
  2. Тип возврата

In CTypes , это достигается указанием:

  1. argtypes - список, содержащий каждый тип аргумента ( CTypes ) (в порядке их появления взаголовок функции)
  2. restype - один CTypes type


  • Невозможно указать
  • Неправильное написание (в основном, то же самое, что и в предыдущем пункте)

любой из них (где требуется 1 ), приведет к применению значений по умолчанию: все интерпретируетсяТед как int ( C89 стиль).
Это генерирует Неопределенное поведение
(также применяется, когда неправильно указывает их).

Вы ошиблись argtype (без s в конце).

Исправьте это, и у вас все будет хорошо

Пример :

Для функции func00 , экспортируемой libdll00.dll ( libdll00.so ) со следующим заголовком:

double func00(uint32_t ui, float f, long long vll[8], void *pv, char *pc);

Эквивалент Python будет:

func00 = libdll00.func00
func00.argtypes = [ctypes.c_uint32, ctypes.c_float, ctypes.c_longlong * 8, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char)]
'''
It would be a lot easier (nicer, and in most cases recommended)
  the last element to be ctypes.c_char_p,
  but I chose this form to illustrate pointers in general.
'''
func00.restype = ctypes.c_double

Другие (более разрушительные) результаты того же (или очень похожего) сценария:


Примечание # 1.

Технически, существуют ситуации, когда этоне требуется спецификацияесли ониНо даже тогда лучше указать их, чтобы исключить любую возможную путаницу:

  • Функция без аргументов:

    function_from_dll.argtypes = []
    
  • Функция возвращает void :

    function_from_dll.restype = None
    
...