Python Forum

Full Version: Embedded Python in C++
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
Is there a way to PyImport_Import(PyModule) so that all it's functions or classes are run as directly vs as import?

Sample Python Code (main.py):

class TestClass:
  • def print_hi(self, name):
    print(f"print_hi {name})


if __name__ == '__main__'
  • t = TestClass()
    t.print_hi("test")

in c++ one can import the main.py with:
  • PyImport_Import(pyMain)
and created the class instance with:
  • PyObject_callObject(pyTestClass)

How do one put the class instance behind the same guard as one can in python
if __name__ == '__main__'
  • classs.function...

Right now in all the module imported to c world is running as import, not direct.
Is there way to switch in c world?
(Jul-30-2022, 05:12 AM)Xeno Wrote: [ -> ]How do one put the class instance behind the same guard as one can in python
It is not clear what you want to do. In Python, the if __name__ == '__main__' only means that the following code must be executed only if the current module's name is '__main__'. There is no obstacle to do the same in C++, the code can check the current module's name.

Or perhaps you want to execute a Python files containing if __name__ == '__main__' sections and you want these sections to be executed, which won't work with an import statement. In that case, you could probably use PyRun_File() to run the file in a dictionary where '__name__' --> '__main__'.
[attachment=1867]

I am mainly testing embedding python class in c++, just want to find a way to initilzie the python class that is behined if __name__ == '__main__'

I am not sure how PyRun_File() will help me here, can you show me know how to import a python class with it, and then call functions in the python class?
Here is a (quick and dirty) simple example in C created from the very high level embedding basic example.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    const char *spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
        else:
            print('called out of __main__')
    )"""";
    PyObject *mainmod, *Spam, *spam, *meth;
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                       "print('Today is', ctime(time()))\n");
    PyRun_SimpleString(
        spamclass
    );
    mainmod = PyImport_ImportModule("__main__");
    Spam = PyObject_GetAttrString(mainmod, "Spam");
    spam = PyObject_CallObject(Spam, NULL);
    meth = PyObject_GetAttrString(spam, "func");
    PyObject_CallObject(meth, NULL);
//     printf("%p\n", spam);
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}
Here is the output
Output:
Today is Sat Jul 30 17:56:09 2022 called in __main__
and the makefile
foo.o: foo.c
	gcc -c foo.c -I/usr/include/python3.8 -I/usr/include/python3.8  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.8-uvizni/python3.8-3.8.10=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall -fPIE

a.out: foo.o
	 gcc foo.o -L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu -L/usr/lib  -lcrypt -lpthread -ldl  -lutil -lm -lm -lpython3.8
oh cool, thank you so much for the sample, I will use this to test my wrapper now.

this is really help fully.
I do have a follow up question on the return values

when I import __main__ and then do PyRun all is working good

but now it seems I can no long decode the python return value

class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
I use to be able to decode the return values on the c++ side like this

            PyObject* result= PyObject_CallObject(meth, NULL);
            PyObject* repr = PyObject_Repr(result);
            PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            cout << PyBytes_AS_STRING(str) << endl;
but now str object is NULL?
(Jul-31-2022, 07:26 AM)Xeno Wrote: [ -> ]but now str object is NULL?
Could you post complete C++ code to reproduce the issue? (preferably with makefile)
I was using the sample code as base and trying to return a string from the func.

mainly it seem to fail on:
str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
the returned str object is NULL

int main() {
        const char* spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
        PyObject* mainmod, * Spam, * spam, * meth, * result, * repr, *str;
        const char* s;
        Py_Initialize();
        PyRun_SimpleString("from time import time,ctime\n"
            "print('Today is', ctime(time()))\n");
        PyRun_SimpleString(
            spamclass
        );
        mainmod = PyImport_ImportModule("__main__");
        Spam = PyObject_GetAttrString(mainmod, "Spam");
        spam = PyObject_CallObject(Spam, NULL);
        meth = PyObject_GetAttrString(spam, "func");
        result = PyObject_CallObject(meth, NULL);
        if (!PyUnicode_CheckExact(result)) {
            repr = PyObject_Repr(result);
            str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        else {
            str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        return 0;
}
It works for me. I could not reproduce the bug.
Output:
Today is Sun Jul 31 22:52:54 2022 called in __main__ response from python: Completed
Makefile:
Output:
# foo.o must come before the libraries when linking a.out: foo.o g++ foo.o -o a.out -L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu -L/usr/lib -lpython3.8 -lcrypt -lpthread -ldl -lutil -lm -lm foo.o: foo.cpp g++ foo.cpp -c -I/usr/include/python3.8 -I/usr/include/python3.8 -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.8-uvizni/python3.8-3.8.10=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O3 -Wall -fpie clean: rm -f a.out foo.o
Full C++ code
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include <iostream>
using namespace std;

int main() {
        const char* spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
        PyObject* mainmod, * Spam, * spam, * meth, * result, * repr, *str;
        const char* s;
        Py_Initialize();
        PyRun_SimpleString("from time import time,ctime\n"
            "print('Today is', ctime(time()))\n");
        PyRun_SimpleString(
            spamclass
        );
        mainmod = PyImport_ImportModule("__main__");
        Spam = PyObject_GetAttrString(mainmod, "Spam");
        spam = PyObject_CallObject(Spam, NULL);
        meth = PyObject_GetAttrString(spam, "func");
        result = PyObject_CallObject(meth, NULL);
        if (!PyUnicode_CheckExact(result)) {
            repr = PyObject_Repr(result);
            str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        else {
            str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        return 0;
}
that is wired, the 2 diffrence I can see is

I am running this in vs2019, and running with python 3.10

is there anything linker wise i need to add in vc++?

this is the C++ options
/JMC /permissive- /ifcOutput "x64\Debug\" /GS /W3 /Zc:wchar_t /I"C:\Program Files\Python310\include" /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc142.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\cppTester.pch" /diagnostics:column

Linker options
/OUT:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.exe" /MANIFEST /NXCOMPAT /PDB:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X64 /INCREMENTAL /PGD:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Debug\cppTester.exe.intermediate.manifest" /LTCGOUT:"x64\Debug\cppTester.iobj" /ERRORREPORT:PROMPT /ILK:"x64\Debug\cppTester.ilk" /NOLOGO /LIBPATH:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug" /LIBPATH:"C:\Program Files\Python310\libs" /TLBID:1
Pages: 1 2