Как я могу вернуть массив wchar_t ** - с нулевым символом в конце строки UNICODE - в скрипт Python с использованием CTypes - PullRequest
0 голосов
/ 21 ноября 2019

Учитывая две функции, реализованные в общей библиотеке с именем c-lib.so, как я могу правильно определить / обернуть функции, используя CTypes.

set_list_of_strings(wchar_t** list);

wchar_t** get_list_of_strings();

Для функции set при добавлении в модуль оболочки Python работает следующее:

import ctypes

_lib = ctypes.CDLL('c-lib.so')

def set_list_of_strings(list):
    global _dsl
    arr = (ctypes.c_wchar_p * len(list))
    arr[:] = list
    _lib.set_list_of_strings(arr)

У меня есть два вопроса, связанных с приведенным выше.

  1. Как бы я определил `set_list_of_strings.argtypes [??] для проверки / ограничения ввода.
  2. Как бы я определил функцию-обертку для правильного возвращения списка строк.

Спасибо

1 Ответ

0 голосов
/ 21 ноября 2019

Я сделал простой пример, когда код C (фактически обернутый C ++) получает список строк и копирует их, затем функция get_list_of_strings возвращает их с суффиксом позиции строки.

Что касается определенияфункции в python:

    # wchar_t** get_list_of_strings()
    get_list_of_strings.restype = ctypes.POINTER(ctypes.c_wchar_p)

    # void set_list_of_strings(wchar_t** list)
    set_list_of_strings.argtypes = (ctypes.POINTER(ctypes.c_wchar_p), )

Способ получить строку из wchar_t** довольно прост: вы можете индексировать сам возвращаемый указатель и извлекать из него строки. Поскольку возвращаемый массив заканчивается символом nullptr (преобразованным в None), вы можете просто остановить итерацию в этой точке:

    result = get_list_of_strings()
    index = 0
    while True:
        p = result[index]
        if p is None:
            break
        index += 1
        print(p)

Сложная часть инициализирует массив указателя wchar_t*:

def list_to_wchar_t_pp(l):
    if not l:
        return None
    buffers = [ctypes.create_unicode_buffer(s) for s in l]
    addresses = list(map(ctypes.addressof, buffers))
    addresses.append(0) # append nullptr at the end
    pp = (ctypes.c_wchar_p * (len(l) + 1))(*addresses)
    return pp

Мы создаем буферы Unicode из строк, получаем адреса этих буферов, добавляем нулевой указатель в конце. Затем мы инициализируем массив указателей с этими адресами.

Код Python:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import string
import ctypes

def list_to_wchar_t_pp(l):
    if not l:
        return None
    buffers = [ctypes.create_unicode_buffer(s) for s in l]
    addresses = list(map(ctypes.addressof, buffers))
    addresses.append(0) # append nullptr at the end
    pp = (ctypes.c_wchar_p * (len(l) + 1))(*addresses)
    return pp


def main():
    dll = ctypes.WinDLL(r"TestDll.dll")

    # wchar_t** get_list_of_strings()
    get_list_of_strings = dll.get_list_of_strings
    get_list_of_strings.restype = ctypes.POINTER(ctypes.c_wchar_p)

    # void set_list_of_strings(wchar_t** list)
    set_list_of_strings = dll.set_list_of_strings
    set_list_of_strings.argtypes = (ctypes.POINTER(ctypes.c_wchar_p), )

    strings = ["foo", "bar", "baz"]
    pp_strings = list_to_wchar_t_pp(strings)
    if not pp_strings:
        return -1

    set_list_of_strings(pp_strings)

    result = get_list_of_strings()
    index = 0
    while True:
        p = result[index]
        if p is None:
            break
        index += 1
        print(p)

    return 0



if __name__ == "__main__":
    sys.exit(main())


Вывод:

foo #1
bar #2
baz #3

C ++ код дляобщая библиотека (Windows DLL);обратите внимание, что освобождение глобального буфера и его указателей также должно быть реализовано.

#include <cstdlib>
#include <string>
#include <iostream>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

static wchar_t** g_list;

EXTERN_DLL_EXPORT void set_list_of_strings(wchar_t** list)
{
    auto count = 0;
    while(true)
    {
        const auto p = list[count];
        if (p == nullptr)
            break;
        count++;
    }

    if (count == 0)
        return;

    g_list = new wchar_t* [count + 1];
    for(int i = 0; i < count; i++)
    {
        const auto s = list[i];
        const auto len = wcslen(s);
        auto p = new wchar_t[len + 1];
        wcscpy_s(p, len + 1, s);
        g_list[i] = p;
    }

    g_list[count] = nullptr;
}

EXTERN_DLL_EXPORT wchar_t** get_list_of_strings()
{
    if (g_list == nullptr)
        return nullptr;

    auto count = 0;
    while(true)  {
        const auto p = g_list[count];
        if (p == nullptr)
            break;
        count++;
    }

    if (count == 0)
        return nullptr;

    const auto new_list = new wchar_t* [count + 1];
    for(int i = 0; i < count; i++)
    {
        std::wstring ws(g_list[i]);
        ws += L" #" + std::to_wstring(i + 1);
        const auto length = ws.length();

        const auto buff_len = ws.length() + 1; 
        const auto buff = new wchar_t[buff_len];

        wcscpy_s(buff, ws.length() + 1, ws.data());

        new_list[i] = buff;
    }

    new_list[count] = nullptr;

    return new_list;
}
...