Skip to main content
gamedev.net
🔒 Locked

Script Language Help!:

Started by PinguinDude Nov 7, 2004 at 7:34 AM 5 replies 600+ views
Original Post
PinguinDude
PinguinDude
Hello :) I wanted to make a small script language I could use. So I started to try some Pointer Function stuff.. I actually forgot what it's called. Anyway the problem is this.

#include <windows.h>
#include <stdio.h>

class CScript
{
public:
	void AddFunction(int number, void(*callback)(void*, void*))
	{
		Ccallback[number] = callback;
	}
	void CallFunction(int number, void *uarg, void *uarg2)
	{
		Ccallback[number](uarg,uarg2);
	}

private:
	void(*Ccallback[10])(void*, void*); // void(*Ccallback[10])(void*);
};

void Message(void *arg, void *arg2)
{
	char *ParamValue = (char*)arg;
	char *ParamValue2 = (char*)arg2;

	MessageBox(NULL, ParamValue, ParamValue, MB_OK);
	MessageBox(NULL, ParamValue2, ParamValue2, MB_OK);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	CScript script;

	script.AddFunction(1, Message);
	script.CallFunction(1, "Yo", "BOEEE");
	
	return 0;
}

I want to be able to pass unlimited args but I really don't know how I could do that :o. If Anyone can help me with that I can add this to the script thing without problems ;)
http://sourceforge.net/projects/pingux/ <-- you know you wanna see my 2D Engine which supports DirectX and OpenGL or insert your renderer here :)
Aldenar
Aldenar
Hi,

One way I can think of is to have some kind of parameter stack, it would require changes to your design though:

class CScriptStack{public:  ...  void PushInt(int val);  int  PopInt(void);  ...};CScriptStack g_ScriptStack;


I made it global and integer-only for the sake of the example, but creating a more flexible stack manager isn't that hard :)

Then in your code, you could do something like:

// The function you register in CScriptvoid MyScriptFunction(){  int param1;  int param2;  // It is a stack, so pop the values in the inverse order  param2 = g_ScriptStack.PopInt();  param1 = g_ScriptStack.PopInt();  printf("MyScriptFunction(%d, %d)\n", param1, param2);}// When calling a functionscript.AddFunction(1, MyScriptFunction);g_ScriptStack.PushInt(1);g_ScriptStack.PushInt(2);script.CallFunction(1);// In the output, you should get://   MyScriptFunction(1, 2)


A downside of that approach is that type checking is harder and you must make sure the caller and the actual function are both expecting the same parameters, otherwise you'll get funny results :)
darookie
darookie
I'd make a more generic value list (also with static type checking):

#include <cassert>#include <iostream>#include <string>#include <vector>#include <boost/shared_ptr.hpp>// extremly oversimplified base class to provide some type info.struct CParamBase {    int Type;    CParamBase(const int type) : Type(type) {}};// some basic typesstatic const int boolType = 0;static const int charType = 1;static const int intType = 2;static const int floatType = 3;static const int strType = 4;static const int ptrType = 5;// actual templated implementationtemplate<typename T> struct CParam : public CParamBase {     T Value;     // the constructor is only declared, not implemented     CParam(T);};// constructor implementations for each supported value type:// the value will be set as well as the correct type-idtemplate<> CParam<char>::CParam(char c) : CParamBase(charType), Value(c) {}template<> CParam<bool>::CParam(bool b) : CParamBase(boolType), Value(b) {}template<> CParam<int>::CParam(int i) : CParamBase(intType), Value(i) {}template<> CParam<float>::CParam(float f) : CParamBase(floatType), Value(f) {}template<> CParam<std::string>::CParam(std::string str) : CParamBase(strType), Value(str) {}template<> CParam<void *>::CParam(void *ptr) : CParamBase(ptrType), Value(ptr) {}//...// the parameter list holds any number of parametersclass CParamList {    typedef std::vector < boost::shared_ptr<CParamBase> > TValueList;    TValueList mValues;public:    template <typename T>    size_t Add(const T& v) {         boost::shared_ptr<CParamBase> p(new CParam<T>(v));	 mValues.push_back(p);         return mValues.size() - 1;    }    template <typename T>    T Get(size_t index) const {         assert(index < Count());         return reinterpret_cast<CParam<T>*>(mValues[index].get())->Value;    }    const CParamBase & operator [] (size_t i) const {         assert(i < Count());         return *mValues.get();    }    size_t Count() const { return mValues.size(); }};// sample function. the Get() method must be used with care -// according to the associated type information, the correct cast// must be provided.void GenericFun(const CParamList &params) {   size_t n = params.Count();   std::cout << n << " parameters passed." << std::endl;   for (size_t i = 0; i < n; ++i) {      switch (params.Type) {         case charType:            std::cout << "#" << i << ": " << params.Get<char>(i) << " (char) " << std::endl;            break;         case boolType:            std::cout << "#" << i << ": " << params.Get<bool>(i) << " (bool) " << std::endl;            break;         case intType:            std::cout << "#" << i << ": " << params.Get<int>(i) << " (int) " << std::endl;            break;         case floatType:            std::cout << "#" << i << ": " << params.Get<float>(i) << " (float) " << std::endl;            break;         case strType:            std::cout << "#" << i << ": " << params.Get<std::string>(i) << " (str) " << std::endl;            break;         case ptrType:            std::cout << "#" << i << ": " << params.Get<void*>(i) << " (ptr) " << std::endl;            break;         default:            std::cout << "#" << i << ": " << params.Get<void*>(i) << " (unknown) " << std::endl;            break;     }   }}// test program driverint main(int argc, char **argv) {    CParamList params;    // add some parameters    params.Add(10);    params.Add(3.1415f);    params.Add(std::string("hello!"));    params.Add(1 == 2);    params.Add('x');    params.Add(42);    // tricky! since no "CParamList"-template is provided,    // we need to cast to a known type (void*)    params.Add<void*>(&params);    GenericFun(params);    return 0;}
PinguinDude
PinguinDude
Ok. I will take a look at this all. If I still can't figure it out well then I will ask again and hope people know an other way of doing it ;)
http://sourceforge.net/projects/pingux/ <-- you know you wanna see my 2D Engine which supports DirectX and OpenGL or insert your renderer here :)
Houdini
Houdini
Personally, for a simple scripting language, I would use function objects instead of functions, and use an object factory to store them. You can read my article on creating a generic object factory if you are unfamiliar with them.

You could then create your commands like this:

// Our base command classclass Command{public:	virtual ~Command() {}	virtual void Execute() = 0;};// This command takes no params and just walks the character forwardclass WalkForwardCommand : public Command{public:	WalkForwardCommand() {}	virtual ~WalkForwardCommand() {}	virtual void Execute()	{		cout << "\nWalking forward...\n" << endl;	}};istream &operator <<(istream &stream, WalkForwardCommand &cmd){	// We don't need to do anything because WalkForwardCommand has no params.	return stream;}// This command simply takes a string param and prints itclass SayCommand : public Command{public:	SayCommand() 	: m_text()	{	}	SayCommand(string text) 	: m_text(text)	{	}	virtual ~SayCommand() {}	virtual void Execute()	{		cout << "\nSaying " << m_text << "...\n" << endl;	}private:	string m_text;};istream &operator <<(istream &stream, SayCommand &cmd){	// We need to read in the text string from the stream.        stream >> cmd.m_text;	return stream;}// This command exits the applicationclass ExitCommand : public Command{public:	ExitCommand() {}	virtual ~ExitCommand() {}	virtual void Execute()	{		exit(1);	}};istream &operator <<(istream &stream, ExitCommand &cmd){	// We don't need to do anything because ExitCommand has no params.	return stream;}


Notice that we feed the params to the class via the operator<<() function. This works great since the user will be typing "command param1, param2, param3", etc. We'll simply read in the command name, create the command class, then let the class itself read in the parameters one by one.

Anyways, we'd use this command factory like so:
// These are the strings used to uniquely identify each function object.static string walk_forward_command = "walk_forward";static string say_command = "say";static string exit_command = "exit";...// We create our object factorytypedef ObjectFactory<Command, string> CommandFactory;CommandFactory command_factory;...// We need to register each function object with our object factorycommand_factory.Register<WalkForwardCommand>(walk_forward_command);command_factory.Register<SayCommand>(say_command);command_factory.Register<ExitCommand>(exit_command);...// This shows you a simple quake like console.  If a user types in "walk_forward" then the WalkForwardCommand class gets created and executed.// If they type in "say hi" then the SayCommand gets created and executed and displays "hi" on the screen.// If the user types in "exit", then the program quits.//// You can add any other command you want simply by deriving from Command and registering the class with the command_factory as shown above.int main(int argc, char* argv[]){	string command;	while (1)	{	cout	<< "\n"		<< "Welcome to the console!\n"		<< "\n"		<< "? ";	cout.flush();	// Read in the command text	cin >> command;	cout << "\n";	// Make the command lowercase for comparisons	std::transform(command.begin(), command.end(), command.begin(), tolower);	// Let's retrieve the command class the user wants to execute	Command *cmd = command_factory.Create(command);	// If a valid command was entered...	if (cmd != NULL)	{		// Let the command class itself read in any additional parameters it needs from the cin stream.		cin >> *cmd;		cmd->Execute();	}	else		cout << "\nUnknown command!" << endl;	return 0;}



- Houdini
Houdini
PinguinDude
PinguinDude
I am more looking for a C style way of doing it. I don't like the way you do it :P
http://sourceforge.net/projects/pingux/ <-- you know you wanna see my 2D Engine which supports DirectX and OpenGL or insert your renderer here :)
PinguinDude
PinguinDude
BUMP :P
http://sourceforge.net/projects/pingux/ <-- you know you wanna see my 2D Engine which supports DirectX and OpenGL or insert your renderer here :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.