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]
Related
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.
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 am writing a Test to keep Data in a C++ map. I am using the Python C-Api.
Adding int values and getting the length of the map is no problem.
But when i get a value from the map and want to return it to Python than the Interpreter crashes.
This is my Code:
//some includes
int VerboseLog = 99;
typedef map<const char*, int> SessionKeeper;
static SessionKeeper openSessions;
static PyObject* getSession(PyObject* self, PyObject* args) {
cout << "get" << endl;
const char *key;
if (!PyArg_ParseTuple(args, "z*", &key)) {
PyErr_SetString(PyExc_Exception, "UUID konnte nicht ausgelesen werden");
PyErr_PrintEx(1);
return NULL;
}
int r = openSessions.at(key);
return Py_BuildValue("i", r);
}
static PyObject *addSession(PyObject* self, PyObject* args) {
cout << "Hello" << endl;
const char *key;
int toAppend;
if (!PyArg_ParseTuple(args, "si", &key, &toAppend)) {
PyErr_SetString(PyExc_Exception, "Ein Parameter konnte nicht ausgelesen werden");
PyErr_PrintEx(1);
return NULL;
}
openSessions[key] = toAppend;
cout << openSessions.size() << endl;
cout << openSessions[key] << endl;
Py_RETURN_NONE;
}
static PyObject* length(PyObject *self){
cout << "length: ";
int i = openSessions.size();
return Py_BuildValue("i",i);
}
static PyMethodDef SessionKeeper_methods[] = {
/*Note the third entry (METH_VARARGS). This is a flag telling the interpreter the calling convention
to be used for the C function. It should normally always be METH_VARARGS or METH_VARARGS | METH_KEYWORDS;
a value of 0 means that an obsolete variant of PyArg_ParseTuple() is used.*/
{ "length", (PyCFunction)length, METH_VARARGS, "return length of a Session" },
{ "addSession", (PyCFunction)addSession, METH_VARARGS, "add a Session." },
{ "getSession", (PyCFunction)getSession, METH_VARARGS, "get a Session" },
{ NULL }
};
static PyModuleDef sessionKeeperMod = {
PyModuleDef_HEAD_INIT,
u8"SessionKeeper",
NULL,
-1,
SessionKeeper_methods
};
static PyModuleDef CpluplusModules_ModDef = {
PyModuleDef_HEAD_INIT,
u8"CPlusPlusMouldes",
u8"Enthält alle Erweietrungen",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC PyInit_CPlusPlusModules(void) {
PyObject* m, *sessionMod;
if (PyType_Ready(&TAModulType) < 0)
return NULL;
if (PyType_Ready(&DatabaseReader_Type) < 0)
return NULL;
if (!(sessionMod = PyModule_Create(&sessionKeeperMod))) {
cout << "fail" << endl;
return NULL;
}
m = PyModule_Create(&CpluplusModules_ModDef);
if (m == NULL)
return NULL;
Py_INCREF(&TAModulType);
Py_INCREF(&DatabaseReader_Type);
PyModule_AddObject(m, "TAModul", (PyObject *)&TAModulType);
PyModule_AddObject(m, "DBReader", (PyObject *)&DatabaseReader_Type);
PyModule_AddObject(m, "SessionKeeper", sessionMod);
return m;
}
The other Modules (DBReader and TAModule) workes fine. The goal will be to safe PythonObjects (containing DbReader Objects and TAModul Objects) in the map.
But bacck to my question why do the getSession crashes the Interpreter on return? And why do the length function works fine.
map<const char*,...> will match based on the address of the string rather than the contents. Therefore it's pretty likely that at will fail and throw an C++ out_of_range exception.
You should start by wrapping the line
int r = openSessions.at(key);
with a try {} catch block, to stop the out_of_range exception propagating through the Python interpreter (which isn't C++ so can't handle it).
You should then change the map to match the key on the string contents. The easiest way would be to use map<std::string,int>.
I was trying to create a Python class definition in C++ code and access it in Python. However, the function is called but the parameters are not received correctly. Please help me in doing this properly.
#include <iostream>
#include <Python.h>
using namespace std;
#include <Python.h>
static PyObject* MyClass__Init(PyObject *self, PyObject *args)
{
cout << "MyClass__Init Called" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyObject* MyModule__Start(PyObject *self, PyObject *args)
{
const char* zBuff;
if (PyArg_ParseTuple(args, "s", &zBuff))
cout << "MyModule Start Called with parameter " << zBuff << endl;
else
cout << "MyModule Start ERROR" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyObject* MyClass__Start(PyObject *self, PyObject *args)
{
const char* zBuff;
if (PyArg_ParseTuple(args, "s", &zBuff))
cout << "MyClass Start Called with parameter" << zBuff << endl;
else
cout << "MyClass Start ERROR" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyMethodDef pModuleMethods[] =
{
{"Start", MyModule__Start, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};
static PyMethodDef pClassMethods[] =
{
{"__init__", MyClass__Init, METH_VARARGS, ""},
{"Start", MyClass__Start, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};
void Start()
{
Py_Initialize();
/* create a new module and class */
PyObject *pClassDic = PyDict_New();
PyObject *pClassName = PyString_FromString("MyClass");
PyObject *pClass = PyClass_New(NULL, pClassDic, pClassName);
PyObject *pModule = Py_InitModule("MyModule", pModuleMethods);
PyObject *pModuleDic = PyModule_GetDict(pModule);
/* add methods to class */
for (PyMethodDef* pDef = pClassMethods; pDef->ml_name != NULL; pDef++)
{
PyObject *pFunc = PyCFunction_New(pDef, NULL);
PyObject *pMethod = PyMethod_New(pFunc, NULL, pClass);
PyDict_SetItemString(pClassDic, pDef->ml_name, pMethod);
}
PyDict_SetItemString(pModuleDic, "MyClass", pClass);
PyRun_SimpleString("import MyModule\n"
"MyModule.Start('Hello Module')\n"
"myObj = MyModule.MyClass()\n"
"myObj.Start('Hello Class')\n");
Py_Finalize();
};
int main()
{
Start();
};
Output is,
MyModule Start Called with parameter Hello Module
MyClass__Init Called
MyClass Start ERROR
The Module function is called without any issue, but the class method is called without the proper input variable.
It appears that the parameter to self is always NULL and instead - for class methods - the reference to self is passed within the argument list.
So, to parse the arguments of a class method you need to parse the reference to self, too.
Since Python 2.6 you can provide a list of format specifiers to PyArg_ParseTuple. The number of format specifiers has to fit the number of arguments that are passed to the function (see https://docs.python.org/2/c-api/arg.html under (items) (tuple) [matching-items]).
By modifying your MyClass__Start function to parse an additional parameter, you are able to parse and print both arguments to inspect them.
For me, the following code
static PyObject* MyClass__Start(PyObject *self, PyObject *args)
{
PyObject* argListSelf;
const char* zBuff;
if (PyArg_ParseTuple(args, "Os", &argListSelf, &zBuff)) {
cout << "MyClass Start Called with parameters " <<
cout << PyString_AsString(PyObject_Str(argListSelf)) <<
cout << " and " << zBuff << endl;
cout << "self " << PyString_AsString(PyObject_Str(self)) << endl;
}
else {
if(PyErr_Occurred())
PyErr_Print();
cout << "MyClass Start ERROR" << endl;
}
Py_INCREF(Py_None);
return Py_None;
};
results in
MyModule Start Called with parameter Hello Module
MyClass__Init Called
MyClass Start Called with parameters \
0x602428<?.MyClass instance at 0x7f484a333200>0x602428 and HelloClass
self <NULL>
Note that I printed the pointer value of self, which is NULL.
I also added
if(PyErr_Occurred())
PyErr_Print();
which I would always add for debugging purposes.
Consider the following example code that I got off the Python documentation page. It takes one argument right now. I want to make it accept a named argument like "mode=2". So that i can call myext.do_something('/home/crypto/something.jpg', mode=2). The argument passed with mode should be an integer and if it gets something else, I want it to raise an error in Python. How do I go about doing this? I'm thinking I should edit module_functions[] but I don't know what to put in there.
#include <Python.h>
static PyObject *
do_something(PyObject *self, PyObject *args)
{
char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
char *result = do_somethingelse(filename);
return Py_BuildValue("s", result);
}
static PyMethodDef
module_functions[] = {
{ "do_something", do_something, METH_VARARGS, "do something" },
{ NULL }
};
PyMODINIT_FUNC
initmyext(void)
{
Py_InitModule("myext", module_functions);
}
This is a minimal example:
#include <Python.h>
static PyObject *
do_something(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *filename;
int mode = 2;
static char *keywords[] = {"filename", "mode", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", keywords, &filename, &mode))
return NULL;
char *result = do_somethingelse(filename);
return Py_BuildValue("s", result);
}
static PyMethodDef
module_functions[] = {
{ "do_something", (PyCFunction)do_something, METH_VARARGS | METH_KEYWORDS, "do something" },
{ NULL }
};
PyMODINIT_FUNC
initmyext(void)
{
Py_InitModule("myext", module_functions);
}