[C++-sig] Re: Type Exception of type Argument Error

Brian Hall bhall at gamers-fix.com
Sun Jul 11 19:35:25 CEST 2004


I'm working on a minimal reproducible test case, but for the time being, let
me post the declarations of the classes involved and what I have so far on
the simple reproduction case:

<code>

class cState : public cObject
{
public:
	typedef vector<cTransition*> tTransitionList;
	typedef vector<cAction*> tActionList;

					cState(float initialValue, string
name) : 
	
mValue(initialValue), 
	
mInitialValue(initialValue),
							mName(name) 
						{}
	virtual 			~cState(void);

	float				Value(void) const { return(mValue);
}
	float				InitialValue(void) const 
					{ return(mInitialValue); }

	void				SetValue(float value) { mValue =
value; }
	void				SetInitialValue(float value) 
					{ mInitialValue = value; }

	void				Reset(void) { mValue =
mInitialValue; }

	const string		&Name(void) const { return(mName); }
	void				SetName(const string &name) { mName
= name; }

	void				Iterate(void)
					{
						for
						(
	
tActionList::iterator it = 
	
mActions.begin();
							it !=
mActions.end();
							++it
						)
							(*it)->Execute();
					}
	tTransitionList		&Transitions(void) { return(mTransitions); }
	tActionList			&Actions(void) { return(mActions); }
	
protected:
	float				mValue;
	float				mInitialValue;
	tTransitionList		mTransitions;
	tActionList			mActions;
	string			mName;
};

class cAction : public cObject
{
public:
				cAction(cState &state) : mState(state) {}
	virtual		~cAction(void) {}
	virtual void	Execute(void) = 0;
	virtual string	Label(void) = 0;
	cState		&State(void) const { return(mState); }

protected:
	cState &mState;
};

class cScriptedAction : public cAction
{
public:
			cScriptedAction(cState &state);
	virtual	~cScriptedAction(void);
};

class cPythonScriptedAction : public cScriptedAction
{
public:
					cPythonScriptedAction(cState
&state);
					cPythonScriptedAction(cState &state,

						const string &scriptName, 
						const string &script);
	virtual			~cPythonScriptedAction(void);

	virtual void	Execute(void);
	virtual string	Label(void) { return("Python Scripted Action"); };

	void			SetScript(const string &script) { mScript =
script; }
	const string	&Script(void) const { return(mScript); }

protected:
	string		mScriptName;
	string		mScript;
	handle<>		mInstance;
};

BOOST_PYTHON_MODULE(GI_AISDK)
{
	// Expose the State class to Python
	class_<cState>("State", init<float, std::string>())
		.add_property("value", &cState::Value, &cState::SetValue)
		.add_property("initial_value", &cState::InitialValue,
&cState::SetInitialValue)
		;

	// Expose a base class new scripted actions should derive from
	class_<cPythonScriptedAction>("PythonScriptedAction",
init<cState&>())
		.def("state", &cScriptedAction::State, 
			return_internal_reference<>())
		;
}

</code>

<pythoncode>

from GI_AISDK import *

class TestAction(PythonScriptedAction):

	def __init__(self):
		pass

	def execute(self):
		self.state()

</pythoncode>

Ok so that's all the code... now for the tricky part.... the way I get all
this stuff to work, is I get the script from the user and I use the C API to
compile it and get it in the dictionary:

bool cPythonScriptEngine::CompileScript(const string &scriptName, 
							const string
&script)
{
	// Parse the script
	node *pythonNode = PyParser_SimpleParseString(script.c_str(),
 					Py_file_input);

	// Dump any errors that may have occurred
	if (PyErr_Occurred())
		return(false);

	// if the script parsed properly, 
	// compile the code and inject it into the dictionary
	PyCodeObject* codeObject = PyNode_CompileFlags(pythonNode, 
		const_cast<char *>(scriptName.c_str()), NULL);
	// free our parsed script node
	PyNode_Free(pythonNode);

	if (!codeObject)
		if (PyErr_Occurred())
			return(false);

	// evaluate the code so the new class can find
	// its way into the dictionary
	PyObject *evaluateResult = PyEval_EvalCode(codeObject,
		mMainNamespace.get(), mMainNamespace.get());
	Py_DECREF(codeObject);
	if (!evaluateResult)
		if (PyErr_Occurred())
			return(false);

	Py_DECREF(evaluateResult);

	return(true);
}

If the code successfully compiles, I instantiate a new cPythonScriptedAction
class and hand it the script name, and construct the instance like so:

cPythonScriptedAction::cPythonScriptedAction
(
	cState &state, 
	const string &scriptName, 
	const string &script
) : 
	cScriptedAction(state), 
	mScriptName(scriptName),
	mScript(script)
{
	mInstance = 
		handle<>(cPythonScriptEngine::Instance().MakeInstance(this, 
			scriptName));
}

PyObject *cPythonScriptEngine::MakeInstance(void *, 
			const string &scriptName)
{
	PyTypeObject *objType = NULL;
	PyObject *newInstance = NULL;
	PyObject *args = PyTuple_New(0);

	// Look up the type object first...
	// Look in the main dictionary for the object
	objType = (PyTypeObject *)PyDict_GetItemString(mMainNamespace.get(),

			const_cast<char *>(scriptName.c_str()));
	// if we don't find it, try the built in dictionary
	if (!objType)
		objType = (PyTypeObject *)PyDict_GetItemString(
				mBuiltinsNamespace.get(), 
				const_cast<char *>(scriptName.c_str()));

	// if we still didn't find it bail
	if (!objType)
		return(NULL);

	// now create a new instance of an object of that type
	if (objType->tp_new)
		newInstance = objType->tp_new(objType, args, NULL);
	else
	{
		// error!
		return(NULL);
	}

	// now call the new objects init method if we have one
	if (!newInstance)
		return(NULL);

	if (objType->tp_init)
	{
		if (objType->tp_init(newInstance, args, NULL) < 0)
			return(NULL);	// init failed?
	}

	return(newInstance);
}

Execute for cPythonScriptedAction is implemented as:

void cPythonScriptedAction::Execute(void)
{
	try
	{
		call_method<void>(mInstance.get(), "execute");
	}
	catch(error_already_set)
	{
		MessageBox(NULL,
	
cPythonScriptEngine::Instance().GetPythonException().c_str(), 
		"Python Execution Error", MB_ICONEXCLAMATION);
	}
}

Given all this, basically a C++ main function would look like:

int main(int argc, char** argv)
{
	const char script[] = 
		"from GI_AISDK import *\n"
		"\n"
		"class TestAction(PythonScriptedAction):\n"
		"\n"
		"\tdef __init__(self):\n"
		"\t\tpass\n"
		"\n"
		"\tdef execute(self):\n"
		"\t\tself.state()\n"
		"\n";

	const char scriptName[] = "TestAction";

	// setup python here
	cPythonScriptEngine::Instance().Initialize();

	cPythonScriptEngine::Instance().CompileScript(scriptName, script);

	cState *state = new cState(10.0f, "TestState");
	cAction *action = new cPythonScriptedAction(*cState, 
					scriptName, script);
	state->Actions().push_back(action);

	try
	{
		state->Iterate();		// will die here evaluating
execute
	}
	catch(...)
	{
	}

	// clean up python here
	cPythonScriptEngine::Instance().Finalize();
}

That's it!  I'm hoping to put together an actual test case that just does
this stuff and put it on my website for download...

If you see anything broken here let me know! Thanks!

Brian Hall

-----Original Message-----
From: c++-sig-bounces at python.org [mailto:c++-sig-bounces at python.org] On
Behalf Of David Abrahams
Sent: Sunday, July 11, 2004 7:10 AM
To: c++-sig at python.org
Subject: [C++-sig] Re: Type Exception of type Argument Error

It's hard to be of much help unless you show the C++ classes being
wrapped.   The best way to get help here is to post a complete,
reproducible, minimal test case.
 




More information about the Cplusplus-sig mailing list