Need help embedding python script in c++

Started by
5 comments, last by ??? 10 years, 1 month ago

Hi, i want to learn how to use a simple python script from c++ but the documentation is a little bit lacking imo. So far i looked here and here, but it's not very clear how it work. For example, do i use PyRun_SimpleString() or PyRun_String()? What's the difference between the two?

What im trying to do is, i have this program that can rename files using lua scripts, and it work perfectly, and i now want to add python as another option to the mix. The job of the script is simple, call a function called convert() that take a given filename as a string and return a modified filename. That way i can rename a batch of files based on a simple script. In lua i had to use globals since trying to push a string before the function call caused problem randomly so if i could do the same in python that would be great.

That's the code im trying to make work (work in progress from my lua code, i want to replace the remaining lua code for python code)


#include "PythonPlugin.h"
#include <string>

string LanguageName = "";

// Those are defined in the script, i need a way to access them
string ScriptName = "";
string InitialDirectory = "";

/**************************************************
main procedure.....................................
**************************************************/
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwreason, LPVOID lpReserved)
{
    switch(fdwreason)
	{
    case DLL_PROCESS_ATTACH: InitializePython(); break;
    case DLL_PROCESS_DETACH: ShutdownPython();   break; 
    case DLL_THREAD_ATTACH:  break;
    case DLL_THREAD_DETACH:  break;
    }
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

void InitializePython()
{
	Py_Initialize();
}

void ShutdownPython()
{
	Py_Finalize();
}

///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

char* EXP_FUNC _GetLanguageName()
{
	LanguageName = "Python";
	return (char*)LanguageName.c_str();
}

///////////////////////////////////////////////////////////////////////////////////////////////

bool EXP_FUNC _LoadScript(char *txt)
{
	//return PyRun_SimpleString(txt) == ;
	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////

char* EXP_FUNC _GetScriptName()
{
	//ScriptName = LuaScript.GetGlobalString("ScriptName");
	//return (char*)ScriptName.c_str();
	return NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////

char* EXP_FUNC _GetInitialDirectory()
{
	//InitialDirectory = LuaScript.GetGlobalString("InitialDirectory");
	//return (char*)InitialDirectory.c_str();
	return NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////

bool EXP_FUNC _Convert(char *in, char *out, char *err)
{
	/*bool res = false;
	ZeroMemory(out, MAX_PATH);
	ZeroMemory(err, 1024);

	LuaScript.SetGlobalString("Input", in); 
	if(LuaScript.SafeCallFunction("Convert", err)){
		
		string Output = LuaScript.GetGlobalString("Output");

		res = Output.length() > 0;
		if(res){
			memcpy(out, Output.c_str(), Output.length()); 
		} else {
			sprintf(err, "%s", "Error: Convert() returned null output.");
		}
	}

	return res;*/
	return false;
}

Advertisement

For example, this is a lua script that rename a batch of files with randoms 16 alphanumerics characters, but keep the extention intact. Notice the globals at the start, i want to be able to access those after calling the Convert function from the script.


ScriptName = "Random"
InitialDirectory = ""

Input = ""
Output = ""

--------------------------------------------------------------------------------------------------------------------
-- Return the char at positon i in s
--------------------------------------------------------------------------------------------------------------------
function GetChar(s, i)
	return string.sub(s, i, i)
end

--------------------------------------------------------------------------------------------------------------------
-- tell if a sub string is withing a string at a given position
--------------------------------------------------------------------------------------------------------------------
function ConcatStr(s1, s2)
	return string.format("%s%s", s1, s2)
end

--------------------------------------------------------------------------------------------------------------------
-- Return the extention of a given file name
--------------------------------------------------------------------------------------------------------------------
function GetExtention(s)
	local sLen = string.len(s)

	local found = false
	local tmp = ""
	local i = sLen
	local j = 0
	while(i > 1) do
		tmp = ""
		tmp = string.sub(s, i, i)
		if(tmp == ".") then
			found = true
			break
		end
		i = i - 1;
	end;

	tmp = ""
	if(found == true) then
		while(i <= sLen) do
			tmp = tmp..string.sub(s, i, i)
			i = i + 1;
		end;
	end;

	return tmp
end


--------------------------------------------------------------------------------------------------------------------
-- Convert the file name (Called from c++)
--------------------------------------------------------------------------------------------------------------------
function Convert(FileName)
	local FileName = Input
	Output = ""

	-- Extract the file extention
	local ext = GetExtention(FileName)

	local tmp = ""
	local i = 1;
	local r = 0
	local c = ""

	while(i <= 16) do

		local k = math.random(2)
		if(k == 1) then
			r = math.random(65,90)
			c = string.char(r)
			tmp = string.format("%s%s", tmp, c)
		end
		if(k == 2) then
			r = math.random(48,57)
			c = string.char(r)
			tmp = string.format("%s%s", tmp, c)
		end

		i = i + 1
	end

	Output = string.format("%s%s", tmp, ext)

	print(Output)
end

math.randomseed(os.time())

That's just an example though, the script can do whatever it want.

For Example, a script that do nothing


ScriptName = "Example"
InitialDirectory = ""

Input = ""
Output = ""

--------------------------------------------------------------------------------------------------------------------
-- Convert the file name (Called from c++)
--------------------------------------------------------------------------------------------------------------------
function Convert(FileName)
	Output = Input
	print(Output)
end

I can make a similar python scripts, im just not sure how to start on the c++ side.

To resume, i need a way to:

-Extract a string value to c++ from python script

-Pass a string value from c++ to python

-Load the script from a char*

-Call the Convert function located in the script

The main problem im having so far is that all the examples seem to load the script from a file, but id like to be able to do it with a char* containing the entire script.

PyRun_SimpleString seem not well suited for what i want to do since it just run a single line of python script at once, or maybe i need to call that for each line of script?

Guess ill start experimenting.

I am a bit rusty on embedding python but I think PyRun_String should do what you want if you pass Py_eval_input or Py_file_input for the start parameter.

Probably not a help with your current problem, but I have used boost python on several occasions embedding in different professional engines and always had good experience. Of course YMMV.

Ok, after playing a bit with this and some extensive googling, i got this test code to work:


#include "Windows.h"
#include "stdio.h"

#include "Python.h"

UINT GetScriptSize(char *pFileName)
{
	char Buf[MAX_PATH];
	ZeroMemory(Buf, sizeof(Buf));
	sprintf(Buf, "%s", pFileName);
    
	WIN32_FIND_DATA Data;
 	ZeroMemory(&Data, sizeof(WIN32_FIND_DATA));
        HANDLE h = FindFirstFile(Buf, &Data);
	if(h != INVALID_HANDLE_VALUE)
		FindClose(h);

	return Data.nFileSizeLow;
}

bool ReadScript(char *pFileName, char *pScriptBuffer)
{
	const int LineBufSize = 4096;
	
	FILE *f = fopen(pFileName, "rt"); 
	if(f){
		char txtline[LineBufSize];
		ZeroMemory(txtline, LineBufSize);
		
		while(fgets(txtline, LineBufSize, f) != NULL)
			strcat(pScriptBuffer, txtline);

		fclose(f);
		return true;
	}

	return false;
}

int main()
{
    // Script file name
    LPCSTR FileName = "C:\\Temp\\Batch Files Renamer\\ScriptTest.py";

    // Extract the text from the file
    const int ScriptBufSize = GetScriptSize((char*)FileName)+1;
    char *pScriptBuffer = new char[ScriptBufSize];
    ZeroMemory(pScriptBuffer, ScriptBufSize);
    
    ReadScript((char*)FileName, pScriptBuffer);

    // Initialize Python
    Py_Initialize();

    // Create module and dictionary
    PyObject* module = PyImport_AddModule("__main__");
    PyObject* dictionary = PyModule_GetDict(module);

    // Run the script
    PyRun_String(pScriptBuffer, Py_file_input, dictionary, dictionary);

    // Call the Convert() function
    PyObject* pFunc = PyObject_GetAttrString(module, "Convert");
    PyObject* result = PyObject_CallObject(pFunc, 0);

    // Retreive the Output variable
    PyObject* output = PyDict_GetItemString(dictionary, "Output");
    PyObject *outconv = PyUnicode_AsEncodedString(output, "utf-8", "Error ~");
    const char *out = PyBytes_AS_STRING(outconv);

    // Print it
    printf("%s\n", out);

    // Shutdown Python
    Py_Finalize();

    // Free allocated memory
    if(*pScriptBuffer)
        delete [] pScriptBuffer;

    // Wait before closing the console window
    system("PAUSE");

    return 0;
}

Now i just need to clean that up and add this to my real code. Thx for pointing out PyRun_String. I still need to find how to modify the "Input" variable in the script but that shouldn't be too hard. Forget that, it works now.

I use ffpython for embeding & extending.

https://github.com/fanchy/ffpython

support python 2.x, only one header files.

This topic is closed to new replies.

Advertisement