Как получить строку из C ++ в python при использовании ctypes и wchar_t? - PullRequest
0 голосов
/ 17 мая 2018

Я могу:

  1. Получить целое число из C ++ и использовать его в python
  2. Отправить строку Python (как wchar_t) в C ++ и выполнить с ней некоторую логику

Я не могу выполнить шаг 2 в обратном направлении.

Вот мой код C ++ (скомпилирован с использованием clion и cygwin в качестве общей библиотеки с использованием C ++ 14).

#include <iostream>

wchar_t aa[2];
extern "C" {
    int DoA()
    {
        return 10;
    }
    int DoB(wchar_t  * in)
    {
        if (in[1] == 'a')
        {
            return 25;
        }
        return  30;
    }

    wchar_t * DoC()
    {
        aa[0] = 'a';
        aa[1] = 'b';
        return aa;
    }
}

Вот мой код на python 3.6.1, который показывает, что я могу, а что нет.Итак, как мне получить мою строку и сделать что-то с ней в Python?Я ожидаю использовать адрес с wstring_at для получения значения, но оно не работает.

from ctypes import *
import os.path
print('Hello')

itExist = os.path.exists('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print(itExist)
lib = cdll.LoadLibrary('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print('dll loaded')

A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)

Вот вывод с ошибкой.

Hello

True

dll loaded

10

25

30

-1659080704

Issue is coming
Traceback (most recent call last):
  File "ShowProblem.py", line 19, in <module>
    Cb = wstring_at(Ca,2)
  File "C:\Users\Daan\AppData\Local\Programs\Python\Python36\lib\ctypes\__init__.py", line 504, in wstring_at
    return _wstring_at(ptr, size)
OSError: exception: access violation reading 0xFFFFFFFF9D1C7000

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Если вы установите .argtypes и .restype ваших функций в оболочке, вы можете вызывать их более естественно. Для обработки выходной строки будет поточно-ориентированным, если вы выделите буфер в Python вместо использования глобальной переменной или просто вернете широкую строковую константу. Вот пример, закодированный для компилятора Microsoft:

test.c

#include <wchar.h>
#include <string.h>

__declspec(dllexport) int DoA(void) {
    return 10;
}

__declspec(dllexport) int DoB(const wchar_t* in) {
    if(wcslen(in) > 1 && in[1] == 'a') // Make sure not indexing past the end.
        return 25;
    return  30;
}

// This version good if variable data is returned.
// Need to pass a buffer of sufficient length.
__declspec(dllexport) int DoC(wchar_t* aa, size_t length) {
    if(length < 3)
        return 0;
    aa[0] = 'a';
    aa[1] = 'b';
    aa[2] = '\0';
    return 1;
}

// Safe to return a constant.  No memory leak.
__declspec(dllexport) wchar_t* DoD(void) {
    return L"abcdefg";
}

test.py

from ctypes import *

# Set up the arguments and return type
lib = CDLL('test')
lib.DoA.argtypes = None
lib.DoA.restype = c_int  # default, but just to be thorough.
lib.DoB.argtypes = [c_wchar_p]
lib.DoB.restype = c_int
lib.DoC.argtypes = [c_wchar_p,c_size_t]
lib.DoC.restype = c_int
lib.DoD.argtypes = None
lib.DoD.restype = c_wchar_p

# Map to local namespace functions
DoA = lib.DoA
DoB = lib.DoB
DoD = lib.DoD

# Do some pre- and post-processing to hide the memory details.
def DoC():
    tmp = create_unicode_buffer(3)  # Writable array of wchar_t.
    lib.DoC(tmp,sizeof(tmp))
    return tmp.value  # return a Python string instead of the ctypes array.

print(DoA())
print(DoB('aaa'))
print(DoB('bbb'))
print(DoC())
print(DoD())

Выход:

10
25
30
ab
abcdefg
0 голосов
/ 17 мая 2018

Я воспроизвел вашу проблему в Linux и исправил ее, определив тип возвращаемого значения из вашей DoC функции:

from ctypes import *
print('Hello')

lib = cdll.LoadLibrary(PATH_TO_TOUR_LIB)
print('dll loaded')
# this line solved the issue for me
lib.DoC.restype = c_wchar_p

A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)

Я также выделил память динамически (некоторые эксперты Python могут прокомментировать это, я полагаючто это вызывает утечку памяти):

extern "C" {
    int DoA()
    {
        return 10;
    }
    int DoB(wchar_t  * in)
    {
        if (in[1] == 'a')
        {
            return 25;
        }
        return  30;
    }

    wchar_t * DoC()
    {
        wchar_t* aa = new wchar_t[2];
        aa[0] = 'a';
        aa[1] = 'b';
        return aa;
    }
}

Дайте мне знать, если он работает в Windows.

...