Jump to content
  • Advertisement
Sign in to follow this  
John Schultz

Squirrel: simple C++ class binding, creation, function calls, and automatic RC

This topic is 4834 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Lua users and/or those looking for a fast and simple way to integrate scripting with a C++ environment (including dynamic, reference-counted class support), see the Squirrel example here. Squirrel's author, Alberto Demichelis (FarCry's Lua script developer) created an example application that animates a teapot in DirectX 9, using 100% Squirrel script. I modifed this example into a simple .lib to allow fast integration and testing with Squirrel script. While many of the binding libraries/methods use template metaprogramming (for example, LuaPlus is powerful+easy to use), this can lead to extra complexity and reduced runtime performance. Additionally, I found the core Squirrel code relatively easy to modify (useful in fixing/upgrading if necessary; compiler and virtual machine are about 6K lines of code). The following is an example class test using Squirrel and the sqplus.lib:
// testSqPlus.cpp

#include
#include
#include "sqplus.h"

struct CTestObj {
  float x;
  float y;
  float z;
  CTestObj() {
    x = y = z = 0.f;
  }
  void update(float t) {
    x += t;
  } // update
  void print(void) {
    char buff[256];
    sprintf(buff,"x: %f\n",x);
//    OutputDebugString(buff);
    puts(buff);
  } // print
};

_DECL_CLASS(TestObj);

_IMPL_NATIVE_CONSTRUCTION(TestObj,CTestObj);

_MEMBER_FUNCTION_IMPL(TestObj,constructor) {
  CTestObj * newv = NULL;
  StackHandler sa(v);
  int nparams = sa.GetParamCount();
  newv = new CTestObj();
  return construct_TestObj(newv);
}

_MEMBER_FUNCTION_IMPL(TestObj,_set) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  const SQChar *s = sa.GetString(2);
  int index = s?s[0]:sa.GetInt(2);
  switch(index) {
  case 0: case 'x': case 'r':
    return sa.Return(self->x = sa.GetFloat(3));
    break;
  case 1: case 'y': case 'g':
    return sa.Return(self->y = sa.GetFloat(3));
    break;
  case 2: case 'z': case 'b':
    return sa.Return(self->z = sa.GetFloat(3));
    break;
  } // switch
  return SQ_ERROR;
}

_MEMBER_FUNCTION_IMPL(TestObj,_get) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  const SQChar *s = sa.GetString(2);
  if(s && (s[1] != 0)) return SQ_ERROR;
  int index = s && (s[1] == 0)?s[0]:sa.GetInt(2);
  switch(index) {
  case 0: case 'x': case 'r': return sa.Return(self->x); break;
  case 1: case 'y': case 'g':    return sa.Return(self->y); break;
  case 2: case 'z': case 'b': return sa.Return(self->z); break;
  } // switch
  return SQ_ERROR;
}

_MEMBER_FUNCTION_IMPL(TestObj,update) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  SQObjectType type = (SQObjectType)sa.GetType(2);
  if (type == OT_FLOAT || type == OT_INTEGER) {
    float t = sa.GetFloat(2);
    self->update(t);
  } else {
    char buff[256];
    sprintf(buff,"Invalid type for CTestObj::update(float): type %d\n",type);
//    OutputDebugString(buff);
    puts(buff);
  } // if
  return 0;
}

_MEMBER_FUNCTION_IMPL(TestObj,print) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  char buff[256];
  sprintf(buff,"x: %f y: %f z: %f\n",self->x,self->y,self->z);
//  OutputDebugString(buff);
  puts(buff);
//  return sa.ThrowError(_SC("Error initializing the device"));
  return 0;
}

_MEMBER_FUNCTION_IMPL(TestObj,_print) {
  _CHECK_SELF(CTestObj,TestObj);
  puts("_print: ");
  return __TestObj_print(v);
}

_BEGIN_CLASS(TestObj)
_MEMBER_FUNCTION(TestObj,constructor,1,"x")  // x = instance ('self/this' not yet created), no arguments.
_MEMBER_FUNCTION(TestObj,_set,3,_T("xs|n"))  // x = instance, string, or int/float, as .x, .y, .z, or [0], [1], [2].
_MEMBER_FUNCTION(TestObj,_get,2,_T("xs|n"))  // x = instance, string, or int/float, as .x, .y, .z, or [0], [1], [2].
_MEMBER_FUNCTION(TestObj,update,2,_T("xn"))  // x = instance (this), n = int or float.
_MEMBER_FUNCTION(TestObj,print,1,"x")        // x = instance (this).
_MEMBER_FUNCTION(TestObj,_print,1,"x")       // x = instance (this).
_END_CLASS(TestObj)

#ifdef SQUNICODE
#define scvprintf vwprintf
#else
#define scvprintf vprintf
#endif

void printfunc(HSQUIRRELVM v,const SQChar *s,...) {
  va_list arglist;
  va_start(arglist, s);
  scvprintf(s, arglist);
  va_end(arglist);
}

class TestSqPlus {
public:
  void init(void) {
    SquirrelVM::Init();
//    sq_setprintfunc(SquirrelVM::GetVMPtr(),printfunc); //sets the print function.
    _INIT_CLASS(TestObj);
  } // if

  TestSqPlus() {
    init();

    try {
//      SquirrelObject main = SquirrelVM::CompileBuffer("local testObj = TestObj(); testObj.print(); testObj.update(\"ab\"); testObj.print()");
      SquirrelObject main = SquirrelVM::CompileBuffer("local LF = \"\\n\"; local testObj = TestObj(); testObj.print(); testObj.update(1.5); testObj.print(); testObj.y += 10.; print(testObj.y+LF); print(\"Array: \"+testObj[0]+LF); print(testObj); print(LF); ");
      SquirrelVM::RunScript(main);
    } // try
    catch(SquirrelError &e) {
      SquirrelVM::DumpStack();
      char buff[256];
      sprintf(buff,"Error: %s, %s\n",e.desc,_SC("Squirrel::TestObj"));
//      OutputDebugString(buff);
      puts(buff);
    } // catch

  }

  ~TestSqPlus() {
    SquirrelVM::Shutdown();
  }

};

void doTest(void) {
  TestSqPlus testSqPlus;
} // doTest

int main(int argc,char * argv[]) {

  // Run twice to make sure cleanup/shutdown works OK.
  puts("Start Pass 1\n");
  doTest();
  puts("Start Pass 2\n");
  doTest();
  puts("Done.\n");

  printf("Press RETURN to exit.");
  getchar();

  return 0;
}



Automatic parameter checking is also implemented. Performance should be comparable to Lua (perhaps faster in some cases where template-based binding utils are used). Squirrel Main Page Squirrel FAQ Getting Started (not using sqplus.lib) Understanding the stack Samples Reference Manual [Edited by - John Schultz on August 21, 2005 8:53:02 PM]

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!