I am struggling with converting from Python str to C++ and back. For Python 2/3 compatibility, I thought using str/bytes for Py2/3, respectively, would suffice (the defines).
Note this is extracted from a larger codebase; apologies for any missing imports.
// C++ stuff compiled to convertor.so
#include "Python.h"
#if PY_MAJOR_VERSION >= 3
#define PyString_Size PyBytes_Size
#define PyString_AsString PyBytes_AsString
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#endif
template<typename T>
struct vec {
T *ptr;
i64 size;
};
extern "C"
vec<uint8_t> str_to_char_arr(PyObject* in) {
int64_t dimension = (int64_t) PyString_Size(in);
vec<uint8_t> t;
t.size = dimension;
t.ptr = (uint8_t*) PyString_AsString(in);
return t;
}
extern "C"
PyObject* char_arr_to_str(vec<uint8_t> inp) {
Py_Initialize();
PyObject* buffer = PyString_FromStringAndSize((const char*) inp.ptr, inp.size);
return buffer;
}
# Python stuff
class Vec(Structure):
_fields_ = [
("ptr", POINTER(c_wchar_p)),
("size", c_long),
]
lib = to_shared_lib('convertor')
lib_file = pkg_resources.resource_filename(__name__, lib)
utils = ctypes.PyDLL(lib_file)
str_to_char_arr = utils.str_to_char_arr
str_to_char_arr.restype = Vec()
str_to_char_arr.argtypes = [py_object]
encoded = str_to_char_arr('abc'.encode('utf-8'))
char_arr_to_str = utils.char_arr_to_str
char_arr_to_str.restype = py_object
char_arr_to_str.argtypes = [py_object.ctype_class]
result = ctypes.cast(encoded, ctypes.POINTER(Vec())).contents
decoded = char_arr_to_str(result).decode('utf-8')
Trying this with 'abc' on python 3.5 seems to yield '\x03\x00\x00' which clearly means something went wrong.
Can anyone spot the issue?
It might be that you expect UCS2 and the Python is configured for UCS4. See also Building an UCS4 string buffer in python 2.7 ctypes
Haven't managed to make this work for Python 2; perhaps someone understands the unicode/str/bytes differences better between the Python versions to fix this. Also this means the issue I have is probably with another package which unfortunately I have no control of atm.
Nevertheless, here is some working code (for me) with Python 3.5 and clang 6.0.
#include "Python.h"
#if PY_MAJOR_VERSION >= 3
#define PyString_Size PyBytes_Size
#define PyString_AsString PyBytes_AsString
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#endif
template<typename T>
struct vec {
T *ptr;
int64_t size;
};
extern "C"
vec<uint8_t> str_to_char_arr(PyObject* in) {
int64_t dimension = (int64_t) PyString_Size(in);
vec<uint8_t> t;
t.size = dimension;
t.ptr = (uint8_t*) PyString_AsString(in);
return t;
}
extern "C"
PyObject* char_arr_to_str(vec<uint8_t> inp) {
Py_Initialize();
PyObject* buffer = PyString_FromStringAndSize((const char*) inp.ptr, inp.size);
return buffer;
}
# Python
from ctypes import *
import pkg_resources
class Vec(Structure):
_fields_ = [
("ptr", POINTER(c_char_p)),
("size", c_long),
]
lib = 'test.so'
lib_file = pkg_resources.resource_filename(__name__, lib)
utils = PyDLL(lib_file)
str_to_char_arr = utils.str_to_char_arr
str_to_char_arr.restype = Vec
str_to_char_arr.argtypes = [py_object]
encoded = str_to_char_arr('Bürgermeister'.encode('utf-8'))
char_arr_to_str = utils.char_arr_to_str
char_arr_to_str.restype = py_object
char_arr_to_str.argtypes = [Vec]
decoded = char_arr_to_str(encoded).decode('utf-8')
print(decoded) # Bürgermeister
Changing c_char_p to c_wchar_p seems to have no effect(?). Still works.
Related
I have a large array in a file that I cannot modify but need to access in C++. I need to iterate through the array and return a value to a python script.
Problem: I am able to find the element in the array by it's name. When I try to return it's value and section elements to the python wrapper they return 0 or -1 no matter what their actual value is. Here is an example of the first element in the array from external_lists.h :
info_struct A1[] = {
{ "LIMITING", {{0x00, 0x02, 0xFF}}
.........
}
This array has thousands of similar elements. When I run the following code I get 0x or -0x1. Valuedll.cpp:
#include <iostream>
#include <string>
#include <array>
#include "external_lists.h"
extern info_struct A1[];
extern info_struct A2[];
extern int A1size;
extern int A2size;
#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT int get_creation_data(const char* needed_name){
int A1_size = ( A1size/ sizeof(A1[0])) ;
int A2_size = ( A2size / sizeof(A2[0])) ;
for (int i = 0; i < A1_size; i++) {
if (A1[i].name == needed_name) {
return A1[i].style->value;
}
}
return -1;
}
The type info_struct is made from the following struct:
struct info_struct{
const char* name;
style_length style[MAX_SIZE];
options_length options[MAX_SIZE];
}
I need to get the values that are inside the style array. The style_length struct is the following:
struct style_length{
uint16_t value, section;
option_bits obits;
}
My python wrapper is the following:
import os, sys, re
from ctypes import *
import ctypes as ct
def get_creation_values(value_name):
valuell = CDLL('C:\\Documents\\creation.dll')
valuell.get_section_data.argtypes = [c_char_p]
valuell.get_section_data.restype = ct.c_int16
return hex(valuell.get_creation_data(value_name))
if __name__ == "__main__":
val = get_creation_values('LIMITING')
print(val)
Output:
(-0x1)
Thanks in advance for any help. If this is too much, thank you for reading. I will try to clarify.
I'm trying to link Fortran|C/C++|Python using VS2010 on Windows 64-bit. I have a main code that is written in Fortran. From that code I call a C++ function and after that I call a Python function to do some staff further. Here is a simplified version of my code:
!Fmain.f90
PROGRAM fmain
IMPLICIT NONE
INTERFACE
SUBROUTINE Call_NN(input_array, output_array) BIND(C, name='Call_NN')
USE, INTRINSIC :: iso_c_binding
IMPLICIT NONE
REAL(C_DOUBLE), INTENT(IN), DIMENSION(*) :: input_array
REAL(C_DOUBLE), INTENT(INOUT), DIMENSION(*) :: output_array
END SUBROUTINE
END INTERFACE
REAL*8, DIMENSION(0:2) :: input_array, output_array
REAL :: b
INTEGER :: i
do i = 1, 3
input_array = 0.01d0
output_array = 0.d0
call Call_NN(input_array, output_array)
enddo
END
The C++ static library is as following:
//Csub.cpp
#include <Python.h>
#include <cassert>
#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <fstream>
#include <array>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
#include <stdlib.h>
using namespace std;
extern "C" void Call_NN(array<double,3> input_array, array<double,3> output_array)
{
// Initialize the Python interpreter.
Py_Initialize();
// Variables Declaration
float result = 0;
float result1 = 0;
float result2 = 0;
float result3 = 0;
float value1 = 0;
float value2 = 0;
float value3 = 0;
// Create some Python objects that will later be assigned values.
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pValue1, *pValue2, *pValue3;
PyObject *pT1, *pT2, *pT3;
// Convert the file name to a Python string.
const char* filename = "module";
pName = PyString_FromString(filename);
if (pName == nullptr)
{
PyErr_Print();
std::exit(1);
}
// Import the file as a Python module.
pModule = PyImport_Import(pName);
if (pModule == nullptr)
{
PyErr_Print();
std::exit(1);
}
// Create a dictionary for the contents of the module.
pDict = PyModule_GetDict(pModule);
if (pDict == nullptr)
{
PyErr_Print();
std::exit(1);
}
// Get the add method from the dictionary.
pFunc = PyDict_GetItemString(pDict, "NN");
if (pFunc == nullptr)
{
PyErr_Print();
std::exit(1);
}
// Create a Python tuple to hold the arguments to the method.
pArgs = PyTuple_New(3);
if (pArgs == nullptr)
{
PyErr_Print();
std::exit(1);
}
// Convert 3 to a Python integer.
value1 = input_array[0];
value2 = input_array[1];
value3 = input_array[2];
pValue1 = PyFloat_FromDouble(value1);
pValue2 = PyFloat_FromDouble(value2);
pValue3 = PyFloat_FromDouble(value3);
// Set the Python int as the first and second arguments to the method.
PyTuple_SetItem(pArgs, 0, pValue1);
PyTuple_SetItem(pArgs, 1, pValue2);
PyTuple_SetItem(pArgs, 2, pValue3);
// Call the function with the arguments.
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
// Print a message if calling the method failed.
if (pResult == NULL)
printf("Calling the add method failed.\n");
// Convert the result to a long from a Python object.
//result = PyFloat_AsDouble(pResult);
pT1 = PyTuple_GetItem(pResult, 0);
pT2 = PyTuple_GetItem(pResult, 1);
pT3 = PyTuple_GetItem(pResult, 2);
// Convert output to float
result1 = PyFloat_AsDouble(pT1);
result2 = PyFloat_AsDouble(pT2);
result3 = PyFloat_AsDouble(pT3);
output_array[0] = result1;
output_array[1] = result2;
output_array[2] = result3;
// Destroy the Python interpreter.
Py_Finalize();
}
And the Python module is defined as:
# module.py
import sys
import numpy as np
import pandas as pd
import pickle
from sklearn.externals import joblib
def NN(a,b,c,):
X_array = np.array([a, b, c])
X = pd.DataFrame(X_array).transpose()
clf = joblib.load('SavedNeuralNetwork.pkl')
y = clf.predict(X)
y = pd.DataFrame(y).transpose()
y_array = pd.DataFrame.as_matrix(y)
i = y_array.item(0)
j = y_array.item(1)
k = y_array.item(2)
output = (i, j, k)
return i, j, k
At the first iteration the program works fine, but at the second iteration I get an unhandled exception at line:
pModule = PyImport_Import(pName);
And in particular:
Unhandled exception at 0x00000001800db3ec in Fmain.exe: 0xC0000005: Access
violation writing location 0x0000000000000002.
Why is this happening? I tried to reload the python module after the first iteration, but the same thing happened. Any suggestions or other comments on this are much appreciated.
The goal is to create a numpy array in c++ and access it in python.
The block of code below runs fine when run as a program.
However, if I use ctypes and run the function, it segfaults on _import_array call. Could someone tell me why this happens ?
#include "Python.h"
#include "numpy/arrayobject.h"
using namespace std;
extern "C"
PyObject* test_create_numpy() {
Py_Initialize();
_import_array();
double* a = new double[1];
a[0] = 1.0;
npy_intp dims = {1};
PyObject *Arr = PyArray_SimpleNewFromData( 1, &dims, PyArray_FLOAT64, a);
return Arr;
}
int main() {
test_create_numpy();
return 0;
}
The python code used:
utils = cdll.LoadLibrary("test.dylib")
test_create_numpy = utils.test_create_numpy
test_create_numpy.restype = py_object
ret_vec = test_create_numpy()
print ret_vec
Good morning,
I have been searching for an answer for quite some time. I hope I simply didn't try the wrong key words. Thanks for the answers! Here is my question:
I'm writing some proof of concept code. I have two C structs. StructureA has a pointer to StructureB. The function bar prints the values to the console. The goal is to initialize and use the structs in Python and pass them to a shared lib, which does some fancy stuff with the structs.
I assume I do something wrong in Python. I tried several ways, as you can see in the (Python-)main function.
Additionally the content of the Strings is mixed up, but that is not my major questions.
This is my C code:
#define _DLL_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct StructureB {
int Value1;
char Value2[80];
} StructureB;
typedef struct StructureA {
int ValueA;
char ValueB[80];
StructureB *ValueC;
} StructureA;
void bar(StructureA *ref){
printf("ValueA: %d\n", ref->ValueA);
printf("ValueB: %s\n", ref->ValueB);
printf("Value1: %d\n", ref->ValueC->Value1);
printf("Value2: %s\n", ref->ValueC->Value2);
}
#ifdef __cplusplus
}
#endif
Here is my Python Code:
#!/usr/bin/python
from ctypes import *
import sys
class StructureB(Structure):
_fields_ = [('Value1', c_int),
('Value2', c_char_p)]
class StructureA(Structure):
_fields_ = [('ValueA', c_int),
('ValueB', c_char_p),
('ValueC', POINTER(StructureB))]
def main(argv):
lib = cdll.LoadLibrary("structures.dll")
sub_struct = StructureB()
sub_struct.Value1 = 42*42
sub_struct.Value2 = c_char_p("Hello StructureB!")
struct = StructureA()
struct.ValueA = -42
struct.ValueB = c_char_p("Hello StructureA!")
#struct.ValueC = pointer(sub_struct)
#struct.ValueC = cast(sub_struct, POINTER(StructureB))
#struct.ValueC = addressof(StructureB)
#struct.ValueC = pointer(StructureB)
struct.ValueC = StructureB
lib.bar(pointer(struct))
print sub_struct.Value2
print struct.ValueB
if __name__ == "__main__":
main(sys.argv)
I have the following function:
void py_get_var( const char** var_name, int* found, char** resultado )
{
*found = 0;
PyObject * module = PyImport_AddModule("__main__");
PyObject * dictionary = PyModule_GetDict(module);
PyObject * result = PyDict_GetItemString(dictionary, *var_name );
if( result == NULL ){
*found = 1;
*resultado = "";
return;
}
#ifdef PY3K
*resultado = PyBytes_AS_STRING( PyUnicode_AsUTF8String(result) );
#else
*resultado = PyString_AS_STRING(result);
#endif
}
which attempts to retrieve a string (it is always a string) from an embedded Python session. It works as intended on Linux, Mac, and Win32 platforms (and several versions of Python).
However, it returns an empty string on Win64. I'm using the GCC compiler on Windows.
Any idea of what can be the reason?