Attis
Attis

Reputation: 623

How to configure and export function that should return char array?

I'm trying to create a function that can be called from e.g. Python through a .dll.

I'm working on byte arrays. This is the original function I'm trying to wrap:

QByteArray getKeys(const QByteArray data, const QByteArray info);

What should the C style interface and implementation look like? Do I have to pass char* arrays and array sizes?

My idea is something along these lines:

__declspec(dllexport) int getKeys(const char* data, int dataSize, const char* info, int infoSize, const char** result);

The function would return the result as a side-effect and the return value is the size of the result.

Upvotes: 1

Views: 136

Answers (1)

CristiFati
CristiFati

Reputation: 41112

Listing [Python.Docs]: ctypes - A foreign function library for Python.

@Jarod42's suggestion wasn't to use std::pair, but to emulate it via a C (compatible) struct.
Here's an example (my dummy getKeys implementation is to simply return the concatenation the 2 QByteArray arguments):

dll00.cpp:

#include <QtCore/QByteArray>

#include <cstdio>
#include <cstring>


#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif


typedef struct {
    size_t size = 0;
    char *data = nullptr;
} CBuffer;

#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API CBuffer getKeysWrapper(const CBuffer data, const CBuffer info);
DLL00_EXPORT_API void freeBuffer(CBuffer *pBuf);

#if defined(__cplusplus)
}
#endif


static QByteArray getKeys(const QByteArray &data, const QByteArray &info)
{
    printf("C++ - data[%d]: %s\n", data.size(), data.constData());
    printf("C++ - info[%d]: %s\n", info.size(), info.constData());
    QByteArray ret(data);
    ret += info;
    printf("C++ - ret[%d]: %s\n", ret.size(), ret.constData());
    return ret;
}

CBuffer getKeysWrapper(const CBuffer data, const CBuffer info)
{
    QByteArray keys = getKeys(QByteArray(data.data, data.size), QByteArray(info.data, info.size));
    CBuffer buf = { static_cast<size_t>(keys.size()), new char[keys.size()] };
    memcpy(buf.data, keys.constData(), buf.size);
    return buf;
}

void freeBuffer(CBuffer *pBuf)
{
    if (!pBuf)
        return;
    delete[] pBuf->data;
    pBuf->data = nullptr;
    pBuf->size = 0;
}

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


CharPtr = ct.POINTER(ct.c_char)


class CBuffer(ct.Structure):
    _fields_ = [
        ("size", ct.c_size_t),
        ("data", CharPtr),
    ]


def main(*argv):
    dll00 = ct.CDLL(DLL_NAME)
    getKeysWrapper = dll00.getKeysWrapper
    getKeysWrapper.argtypes = (CBuffer, CBuffer)
    getKeysWrapper.restype = CBuffer
    freeBuffer = dll00.freeBuffer
    freeBuffer.argtypes = (ct.POINTER(CBuffer),)
    freeBuffer.restype = None

    data_b = b"123456"
    data_b_len = len(data_b)
    info_b = b"abcd\0ef"
    info_b_len = len(info_b)

    data = CBuffer(data_b_len, (ct.c_char * data_b_len)(*data_b))
    info = CBuffer(info_b_len, (ct.c_char * info_b_len)(*info_b))

    keys = getKeysWrapper(data, info)
    print("PY - keys:", keys.size, bool(keys.data), " ".join(str(keys.data[i]) for i in range(keys.size)))
    freeBuffer(ct.byref(keys))
    print("PY - keys(freed):", keys.size, bool(keys.data))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Output:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q069346803]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  dll00.cpp
[064bit prompt]> g++ -fPIC -DQT_NO_VERSION_TAGGING -I/home/cfati/Install/Qt/Qt/5.12.11/gcc_64/include -shared -o dll00.so -L/home/cfati/Install/Qt/Qt/5.12.11/gcc_64/lib dll00.cpp -lQt5Core
[064bit prompt]> ls
code00.py  dll00.cpp  dll00.so
[064bit prompt]> 
[064bit prompt]> LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/cfati/Install/Qt/Qt/5.12.11/gcc_64/lib python code00.py 
Python 3.8.10 (default, Jun  2 2021, 10:49:15) [GCC 9.4.0] 064bit on linux

C++ - data[6]: 123456
C++ - info[7]: abcd
C++ - ret[13]: 123456abcd
PY - keys: 13 True b'1' b'2' b'3' b'4' b'5' b'6' b'a' b'b' b'c' b'd' b'\x00' b'e' b'f'
PY - keys(freed): 0 False

Done.

Upvotes: 1

Related Questions