Skip to content

Instantly share code, notes, and snippets.

@caiorss
Created February 10, 2019 06:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save caiorss/fc0e628daf7bbb4e8484491856a07c38 to your computer and use it in GitHub Desktop.
Save caiorss/fc0e628daf7bbb4e8484491856a07c38 to your computer and use it in GitHub Desktop.
Python Ctypes wrapper to C++ shared library returning dynamically allocated string buffers
// 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);
}
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