C PART
#include <stdlib.h>
#include <iostream>
#include "Python.h"
using namespace std;
char* PyCall(const char* a, const char* b, const char* c) {
Py_Initialize();
if (!Py_IsInitialized())
{
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyObject* pFunc1 = NULL;
PyObject* pFunc2 = NULL;
PyObject* pFunc3 = NULL;
PyObject* pModule = PyImport_ImportModule(a);
if (pModule == NULL)
{
cout << "notfind";
}
pFunc3 = PyObject_GetAttrString(pModule, b);
PyObject* args3 = PyTuple_New(1);
PyObject* args2 = PyBytes_FromString(c);
PyTuple_SetItem(args3, 0, args2);
PyObject* pRet = PyObject_CallObject(pFunc3, args3);
char* result = NULL;
if (pRet)
{
result = PyBytes_AsString(pRet);
}
return result;
}
int main()
{
char* res = PyCall("mytest", "codetest", "{'title':'Task Manager'}");
cout << res;
}
Python Part
def codetest(title):
import win32gui
import win32api
import json
dic = json.loads(title)
a = win32gui.FindWindow(None,dic["title"])
return str(a)
The basic Python library was imported successfully, but a runtime error occurred
enter image description here
Exception thrown at 0x00007ff680271103 (in pycode. Exe): 0xc0000005: an access violation occurred while reading location 0x000000000000000.
This problem has been resolved. The problem of the return value during the call resulted in cout error, and the parameter passed in python is bytes that needs to be decoded and transcoded. I didn’t read the C-API documentation carefully.
Related
From reading another post, I am trying to embbed some some Python code into C:
main.c
#include <Python.h>
int callModuleFunc(int array[], size_t size) {
PyObject *mymodule = PyImport_ImportModule("py_function");
PyObject *myfunc = PyObject_GetAttrString(mymodule, "printlist");
PyObject *mylist = PyList_New(size);
for (size_t i = 0; i != size; ++i) {
PyList_SET_ITEM(mylist, i, PyInt_FromLong(array[i]));
}
PyObject *arglist = Py_BuildValue("(o)", mylist);
PyObject *result = PyObject_CallObject(myfunc, arglist);
int retval = (int)PyInt_AsLong(result);
Py_DECREF(result);
Py_DECREF(arglist);
Py_DECREF(mylist);
Py_DECREF(myfunc);
Py_DECREF(mymodule);
return retval;
}
int main(int argc, char *argv[])
{
int a[] = {1,2,3,4};
callModuleFunc(a, 4);
return 0;
}
py_function.py
'''py_function.py - Python source designed to '''
'''demonstrate the use of python embedding'''
def printlist(mylist):
print mylist
Then I compiled with:
gcc main.c -I/usr/include/python2.7 -lpython2.7
But then I ran the app, it gives me a segmentation fault error:
/a.out
[1] 18890 segmentation fault ./a.out
Is there something that I am missing?
There were several problems with your code:
Py_Initialize() was not called.
PyImport_ImportModule() failed to find your python file, since in embedded Python you start without an initial module, relative to which the search can work. The fix is to explicitly include the current directory in sys.path.
"(O)" in Py_BuildValue() should use a capital 'O'.
The printlist function should return a value (since that is what the C-code expects).
This should work:
main.c
#include <Python.h>
void initPython()
{
Py_Initialize();
PyObject *sysmodule = PyImport_ImportModule("sys");
PyObject *syspath = PyObject_GetAttrString(sysmodule, "path");
PyList_Append(syspath, PyString_FromString("."));
Py_DECREF(syspath);
Py_DECREF(sysmodule);
}
int callModuleFunc(int array[], size_t size) {
PyObject *mymodule = PyImport_ImportModule("py_function");
assert(mymodule != NULL);
PyObject *myfunc = PyObject_GetAttrString(mymodule, "printlist");
assert(myfunc != NULL);
PyObject *mylist = PyList_New(size);
for (size_t i = 0; i != size; ++i) {
PyList_SET_ITEM(mylist, i, PyInt_FromLong(array[i]));
}
PyObject *arglist = Py_BuildValue("(O)", mylist);
assert(arglist != NULL);
PyObject *result = PyObject_CallObject(myfunc, arglist);
assert(result != NULL);
int retval = (int)PyInt_AsLong(result);
Py_DECREF(result);
Py_DECREF(arglist);
Py_DECREF(mylist);
Py_DECREF(myfunc);
Py_DECREF(mymodule);
return retval;
}
int main(int argc, char *argv[])
{
initPython();
int a[] = {1,2,3,4,5,6,7};
callModuleFunc(a, 4);
callModuleFunc(a+2, 5);
Py_Finalize();
return 0;
}
py_function.py
'''py_function.py - Python source designed to '''
'''demonstrate the use of python embedding'''
def printlist(mylist):
print mylist
return 0
This is the Python Code to Execute external python files
exec(open("file.py").read())
How to do it in c
main.cpp
#include <iostream>
#include <string>
#include <list>
class PrivateDriverData {
public:
std::string PythonExecutable = "python3";
std::string exec(std::string command) {
char buffer[128];
std::string result = "";
// Open pipe to file
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
return "popen failed!";
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
};
std::string ExecDriver() {
PrivateDriverData LocalPDD;
std::string ScraperExecuteData = LocalPDD.PythonExecutable + " file.py";
return LocalPDD.exec(ScraperExecuteData);
}
int main() {
std::string answer = ExecDriver();
std::cout << answer << "\n";
}
The closet thing C has is dlopen(). which open a compiled and linked dynamic library and provides a way to run the code it contains.
It's not standard C and requires a hosted environment so it's not going to work on Arduino etc.
I'm testing a scenario that when C++ set a function pointer to a python class variable, and then use PyObject_CallMethod to run another python method, which contain that class variable.
whole process would like this.
(1). PyCFunction_NewEx() make a py function -> (2). PyDict_SetItemString() assign to class variable under __dict__ -> (3). PyObject_CallMethod() call python method witch contain (1).
When I put all the code inside main() function (whitout void setCallback() and all code inside void setCallback() were placed in main()), It runs perfectly fine. However, after I put some code into a function, sometimes get seg fault, sometimes doesn't call function pointer in python and sometimes get correct answer.
How do I resolve this problem?
C++ Code: main.cpp
#include <python3.7/Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <python3.7/methodobject.h>
// func ptr.
PyObject *myCallFunc(PyObject *self,PyObject *args) {
printf(" aaaaaaaaaaaaaaaaaaaaaaa\n");
return NULL;
}
// def func ptr
typedef PyObject *(*PyCallFunc)(PyObject *self,PyObject *arg);
// set func ptr into python member var
void setCallback(PyObject *ClassObj){
PyCallFunc pyCallFunc = myCallFunc;
PyMethodDef methd = {"methd",pyCallFunc,METH_VARARGS,"py call func"};
PyObject *fName = PyUnicode_FromString(methd.ml_name);
if(fName == NULL) {
printf(" fName\n");
exit(0);
}
PyObject *pyRunFunc = PyCFunction_NewEx(&methd,NULL,fName);
if(pyRunFunc == NULL){
printf(" can not create py function. exit.");
exit(0);
}
Py_DECREF(fName);
PyObject* classAttrDict = PyObject_GetAttrString(ClassObj, "__dict__"); // extract instance Dictionary.
if(classAttrDict == NULL) {
printf(" classAttrDict\n");
exit(0);
}
int pRetSetCurrPrice = PyDict_SetItemString(classAttrDict, "callFunc", pyRunFunc);
if(pRetSetCurrPrice != 0){
printf(" set error. exit.");
exit(0);
}
}
int main(int argc,char **argv){
Py_SetProgramName((wchar_t *)argv[0]);
void *pyMem = PyMem_Malloc(sizeof(wchar_t*)*argc);
wchar_t** _argv = (wchar_t**)&pyMem;
for (int i=0; i<argc; i++) {
wchar_t* arg = Py_DecodeLocale(argv[i], NULL);
_argv[i] = arg;
}
Py_Initialize();
PySys_SetArgv(argc, _argv);
PyObject* programName = PyUnicode_FromString("test");
if(programName == NULL) {
printf(" programName\n");
exit(0);
}
PyObject* pCustomFunc = PyImport_Import(programName); // import test
Py_DECREF(programName);
if(pCustomFunc == NULL) {
printf(" pCustomFunc\n");
exit(0);
}
PyObject* pClass = PyObject_GetAttrString(pCustomFunc, "Test"); // pClass = test.Test
if(pClass == NULL) {
printf(" pClass\n");
exit(0);
}
PyObject* pNewInstance = PyObject_CallObject(pClass,NULL); // pNewInstance = test.Test()
if(pNewInstance == NULL) {
printf(" pNewInstance\n");
exit(0);
}
setCallback(pNewInstance);
PyObject* pCallRet = PyObject_CallMethod(pNewInstance, "runCustomFunc",NULL); // pCallRet = pNewInstance.callFunc()
if(pCallRet == NULL) {
printf(" pCallRet\n");
//exit(0);
}
sleep(2);
printf(" \n\nend\n\n");
Py_Finalize();
return 0;
}
Python code: test.py
import sys
def dummyFunc():
pass
class Test:
def __init__(self):
self.aaa = 0
self.callFunc = dummyFunc
def runCustomFunc(self):
print(" print from python.")
print(" ref count of self.callFunc 1 is %d" %(sys.getrefcount(self.callFunc)))
self.callFunc()
print(" ref count of self.callFunc 2 is %d" %(sys.getrefcount(self.callFunc)))
return 1
cmake for this test project: CMakeLists.txt
# set cmake and compiler.
cmake_minimum_required(VERSION 3.12...3.15)
set(CMAKE_CXX_FLAGS -std=c++17)
# set variable
set(CMAKE_POSITION_INDEPENDENT_CODE ON) # test if this can resolve the problem
set(THREADS_PREFER_PTHREAD_FLAG ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra") # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_DEBUG "-g") # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_RELEASE "-O0") # test if optimize cause the problem
set(LINK_LIB "/usr/local/lib")
set(PYTHON3_LINKER "-lpython3.7")
#set(PTHREAD "-lpthread")
set(PYTHON3_HEADER "/usr/include/python3.7")
set(PYTHON3_LIB "/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu")
set(CPP_FILE_LIST "main.cpp")
include_directories( ${PYTHON3_HEADER})
link_directories( ${PYTHON3_LIB} ${LINK_LIB})
add_executable(pyEmbedFunPtrTest ${CPP_FILE_LIST})
target_link_libraries(pyEmbedFunPtrTest ${PYTHON3_LINKER})
find_package(Threads REQUIRED)
target_link_libraries(pyEmbedFunPtrTest Threads::Threads)
#target_compile_options(pyEmbedFunPtrTest PUBLIC "-pthread")
It could be because the PyMethodDef is created on the stack of the setCallback
You can verify it in the source code of cpython here.
the PyMethodDef is not copied, it is referenced instead.
I am trying to pass a list to python from cpp and taking it back. Initially I tried to pass a single value and get back one value. It worked. Now I am trying to pass the complete array/list Below is my cpp code:
#include <iostream>
#include <Python.h>
#include <numpy/arrayobject.h>
#include <typeinfo>
using namespace std;
int main()
{
Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyString_FromString("."));
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pValue;
// Build the name object
pName = PyString_FromString("mytest");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyObject_GetAttrString(pModule, "stuff");
if (!PyCallable_Check(pFunc))
PyErr_Print();
PyObject *list = PyList_New (5);
Py_ssize_t size = PyList_GET_SIZE(list);
for(Py_ssize_t s = 0; s < size; s++ )
{
PyList_SetItem(list, s, Py_BuildValue("d", 2.5));
}
PyObject* result = PyObject_CallObject(pFunc, list);
if(result==NULL)
{cout << "FAILED ..!!" << endl;}
cout << result << endl;;
return 0;
}
I am always getting "FAILED..!!".
Here is my mytest.py
def stuff(a):
x=a
return x
Any suggestions where I might be going wrong?
From the documentation:
PyObject* PyObject_CallObject(PyObject *callable, PyObject *args)
This is the equivalent of the Python expression: callable(*args).
Whereas PyObject_CallFunctionObjArgs is documented as:
PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ..., NULL)
This is the equivalent of the Python expression: callable(arg1, arg2, ...).
So change your call to the following:
PyObject* result = PyObject_CallFunctionObjArgs(pFunc, list, NULL);
(or you could wrap your list inside another list and keep on using CallObject, but this is by far the easier solution)
I'm trying to export some C++ functions into python, and it's working pretty well most of the time, but I'm having issues when a function takes more than on string in parameters, the first one is always ok, but the other ones are always gibberish.
C++ code:
#include <Python.h>
#include <iostream>
PyObject* wrap_one_string(PyObject *self, PyObject *args)
{
char* str;
if (!PyArg_ParseTuple(args,"s:wrap_one_string",&str))
{
return nullptr;
}
std::cout << str << std::endl;
return Py_None;
}
PyObject* wrap_two_string(PyObject *self, PyObject *args)
{
char* str1, str2;
if (!PyArg_ParseTuple(args,"ss:wrap_one_string", &str1, &str2))
{
return nullptr;
}
std::cout << str1 << std::endl << str2 << std::endl;
return Py_None;
}
static PyMethodDef exampleMethods[] = {
{ "one_string", wrap_one_string, METH_VARARGS, nullptr },
{ "two_string", wrap_two_string, METH_VARARGS, nullptr },
{ nullptr, nullptr, 0, nullptr}
};
extern "C"
{
__declspec(dllexport)
void initexample()
{
PyObject *m;
m = Py_InitModule("example", exampleMethods);
}
}
python code:
import example
example.one_string("ab")
example.two_string("abcd", "efgh")
And the result is:
ab
abcd
È
The second string parameter is always a weird character.
Any idea where this might come from?
Thanks
Ah, nevermind, dumb mistake from my part
char* str1, str2;
should be
char* str1, *str2;
Too bad it compiled, though it did give an error using
str2 = PyString_AsString(PyTuple_GET_ITEM(args, 1))
to access the second string.
One small note, remember to Py_INCREF(Py_None); before returning Py_None, as stated in the reference [https://docs.python.org/2/extending/extending.html#back-to-the-example]
Or use Py_RETURN_NONE macro [https://docs.python.org/2/c-api/none.html#c.Py_RETURN_NONE]