Sign in to follow this  
taylorsnead

Multiple Scripting Languages With Identical User-work

Recommended Posts

I am attempting to make a system that would allow a user (in C++) to bind classes to their scripting language of choice, then compile the script and run it. The primary language I am using for any scripting needs is Squirrel, but I want to be able to switch languages at runtime (possibly with an enum), and using the [b]same[/b] code, bind to, say, Python. I was thinking of doing it by having a class for each language's Class (eg. has Var, Func, Prop, Bind, etc, functions for binding) and a class for it's Script (eg. Compile and Run functions), then instantiating those runs through the Subsystem (which changes based on the enum to a child class with creation methods), which passes back a Class* pointer or Script* pointer, but I'd prefer it to be implicit. Like this:
[source lang="cpp"]void BindPlayer()
{
scriptLang = "Squirrel";
// SClass is for ScriptClass, the parent top class, not for Squirrel specifics
SClass<Player> pclass;
pclass.Var("TestVar", &Player::TestVar);
// Bind other vars/funcs/props
pclass.Bind("Player");

Script scr;
scr.Compile("TestPlayer.nut");
scr.Run();

scriptLang = "Python";
// Do the bind again, same code.
Script scrp;
scrp.Compile("TestPlayer.py");
scrp.Run();
}[/source]
How feasible is this method; if at all, how exactly would I go about the implicit creation via another class? If that method wouldn't work well, then what would?

Share this post


Link to post
Share on other sites
Supporting multiple languages should not be a technical problem at all, when using some kind of clean interface and binding. Something like this:

[source lang="cpp"]class ScriptInterface
{
startNewCall( string pName);
addParam(int pIntParam);
addParam(string pStringParam);
ScriptResult executeCall();
}

class ScriptSquirrel : ScriptInterface
{
// implement script binding etc.
}

class ScriptFactory
{
ScriptInterface createNewScriptInterface( String pLanguage) ;
}
[/source]


But the practical benefit could be lower than expected when [i]mixing [/i]languages, because most scripting languages runs in its own VM including all data.

Share this post


Link to post
Share on other sites
I'm working on a similar system, so I hope it's feasible [img]http://public.gamedev.net//public/style_emoticons/default/wink.png[/img]

I manually reflect on my bindable classes like:[code]class Test1
{
public:
eiBind(Test1);
int DoStuff() { return 0; }
private:
int m_stuff;
};
eiBindClass( Test1 )
eiBeginMethods()
eiBindMethod( DoStuff )
eiEndMethods()
eiBeginData()
eiBindData( m_stuff )
eiEndData()
eiEndBind();[/code]And then to bind it to a specific scripting VM, I can fetch the above info with[code]const TypeBinding& b = ReflectTest1();[/code]Which gives me structures like[code]typedef void (FnTask)( void* obj, void* args, uint argSize );
struct DataBinding
{
const char* name;
memptr memvar;
uint offset, size;
Type type;
};
struct MethodBinding
{
const char* name;
FnTask* task;
const DataBinding* args;
uint argCount;
};
struct TypeBinding
{
const DataBinding* data;
uint dataCount;
const MethodBinding* method;
uint methodCount;
};[/code] Edited by Hodgman

Share this post


Link to post
Share on other sites
I understand both of your concepts. I tried to implement both. For the first (Ashaman73) concept, I did it like so:
[source lang="cpp"]//SClass has Var, Func, Prop, and Bind functions without implementations
//SqClass is a child and implements the functions for squirrel
//ScriptSystem::NewClass() returns a pointer to a child SqClass or eg. LuaClass, based on mLang (set by SetLang();

ScriptSystem::SetLang("Squirrel");
SClass $Player# & scp = ScriptSystem::NewClass<Player>();
scp.Var("Health", &Player::mHealth);
scp.Bind("Player");
Script& sqs = ScriptSystem::NewScript();
sqs.Compile("Test.nut");
sqs.Run();[/source]
Now, this should basically work, except that if I want to, say, shut off the Squirrel VM and launch the Lua VM, I have to rebind those classes. Using something closer to the other given concept (Hodgman), I'd be able to bind once, and it should keep a list of the classes to be bound in ScriptSystem (Since then the SClass holds actual data), then on VM binding (called just before script launching begins), it would take the data from all SClass instances in the list, using it to create VM specific Classes, binding their members and vars, then binding the actual class (Also, then it's easy to remove them from the bind list). The only problem is, I can't really figure out how I should manage the structures to use for data containment, and later usage for binding to the specific VM.
I was thinking something like this:
[source lang="cpp"]
struct VarData
{
string label;
void* ref;
};

struct ClassData
{
vector $VarData# vars;
};

struct SClass
{
template $class T, typename V#
void Var(string label, V T::*val)
{
VarData newvar;
newvar.label = label;
newvar.ref = val;
data.vars.push_back(newvar);
}

void Bind(string label);

ClassData data;
};
[/source]
But, the problem is about using that given data later.
[source lang="cpp"]Class $Player# cls;
for(int i=0; i $ play.data.vars.size(); ++i)
{
cls.Var(play.data.vars[i].label, play.data.vars[i].ref);
RootTable().Bind("NameHere", cls);
}[/source] ("<>" are replaced by "$#", the code block doesnt like those characters, for some reason)
Something like that for Squirrel, except I can't use the void pointer directly, so would I use a different type for storage, or how can I cast to the correct type for this situation? I'm just not really sure how I'd approach it. Edited by taylorsnead

Share this post


Link to post
Share on other sites
I'm going to bump this, because I still haven't figured it out, and I'm still trying to implement it. I'm able to make a system for multiple languages, but it then forces the programmer to rebind their classes for each language. So, I was attempting to do something like what Hodgman suggested, but I don't really know how I'd reaccess the data later, since it would be a solely automatic grabbing and using of the data and I can't store a type as a variable so that it's known what type to bind it as. Once I can have the data grabbed later on in a separate function with the correct type and stuff, then I can have an easily extensible system to add new languages that work with no changes to existing binding code. Maybe it would be better to bind one language (eg. Squirrel) and use its dynamic typing to create a binding system there that routes back to C++, allowing someone to bind their classes in either Squirrel or C++?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this