Created
February 10, 2019 06:19
-
-
Save caiorss/fc0e628daf7bbb4e8484491856a07c38 to your computer and use it in GitHub Desktop.
Python Ctypes wrapper to C++ shared library returning dynamically allocated string buffers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compile with: | |
// $ clang++ cstr.cpp -o cstr.so -std=c++1z -O0 -g -shared -fPIC -std=c++1z | |
//---------------------------------------------------- | |
#include <iostream> | |
#include <string> | |
#include <sstream> | |
#include <cstring> // std::strncpy | |
#include <cmath> | |
#if defined(_WIN32) | |
// MS-Windows | |
#define EXPORT_C extern "C" __declexpec(dllexport) | |
#else | |
// Unix | |
#define EXPORT_C extern "C" | |
#endif | |
/** Design 1: Buffer allocated by the caller. | |
* @param[in] x Float point number | |
* @param[in,out] buffer Buffer allocated by the caller. | |
* @param[in] buffer_size Size of buffer allocated by the caller in bytes. | |
* @return Size of copied array. | |
*/ | |
EXPORT_C size_t | |
square_root_as_stringA(double x, char* buffer, size_t buffer_size) | |
{ | |
std::string ss; | |
ss = ss + "The square root of x = " + std::to_string(x) | |
+ " is equal to " + std::to_string(sqrt(x)); | |
std::strncpy(buffer, ss.data(), buffer_size); | |
return ss.size(); | |
} | |
/** Design 2: Buffer allocated by the caller. If the buffer is null, the function | |
* returns the required buffer size. | |
* @param[in] x Float point number | |
* @param[in,out] buffer Buffer allocated by the caller. | |
* @param[in] buffer_size Size of buffer allocated by the caller in bytes. | |
* @return Size of copied array or required buffer size. | |
*/ | |
EXPORT_C size_t | |
square_root_as_stringB(double x, char* buffer, size_t buffer_size) | |
{ | |
std::string ss; | |
ss = ss + "The square root of x = " + std::to_string(x) | |
+ " is equal to " + std::to_string(sqrt(x)); | |
if(buffer == nullptr) | |
return ss.size(); | |
std::strncpy(buffer, ss.data() /* const char* */, buffer_size); | |
return ss.size(); | |
} | |
/** Design3: Buffer allocated by callee (this code) and released by the | |
* caller. */ | |
EXPORT_C char* | |
square_root_as_stringC(double x){ | |
std::string ss; | |
ss = ss + "The square root of x = " + std::to_string(x) | |
+ " is equal to " + std::to_string(sqrt(x)); | |
return strdup(ss.data()); | |
} | |
EXPORT_C void | |
cstring_delete(char* pCstring) | |
{ | |
std::cout << " [INFO] C-string deleted, memory released. OK." << "\n"; | |
std::free(pCstring); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes | |
CString = ctypes.POINTER(ctypes.c_char) | |
STDString = ctypes.c_void_p | |
CVoid = None | |
dll = ctypes.cdll.LoadLibrary("cstr.so") | |
print("\n EXPERIMENT1 => Call square_root_as_stringA") | |
print("=============================================") | |
dll.square_root_as_stringA.argtypes = [ ctypes.c_double, | |
CString, | |
ctypes.c_size_t ] | |
dll.square_root_as_stringA.restype = ctypes.c_size_t | |
buf_size = 1024 # 1 Kbyte = 1024 bytes | |
buf = ctypes.create_string_buffer(buf_size) | |
n_read = dll.square_root_as_stringA(100.45, buf, buf_size) | |
print("Size = ", n_read, " ; RESULT = ", buf.value) | |
print("\n EXPERIMENT 2 => Call square_root_as_stringB") | |
print("=============================================") | |
dll.square_root_as_stringB.argtypes = [ ctypes.c_double, | |
CString, | |
ctypes.c_size_t ] | |
dll.square_root_as_stringB.restype = ctypes.c_size_t | |
x = 200.45 | |
required_size = dll.square_root_as_stringB(x, ctypes.c_char_p(0), 0) | |
buf = ctypes.create_string_buffer(required_size) | |
dll.square_root_as_stringB(x, buf, required_size) | |
print(" RESULT = ", buf.value) | |
print("\n EXPERIMENT 3 => Call square_root_as_stringC") | |
print("=============================================") | |
dll.square_root_as_stringC.argtypes = [ ctypes.c_double ] | |
dll.square_root_as_stringC.restype = ctypes.c_char_p | |
dll.cstring_delete.argtypes = [ ctypes.c_char_p ] | |
dll.cstring_delete.restype = CVoid | |
hCString = dll.square_root_as_stringC(167.42) | |
print(" RESULT = ", hCString) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment