[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