1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-14 11:47:15 +01:00
SqMod/include/sqrat/sqratClass.h

953 lines
37 KiB
C++

//
// SqratClass: Class Binding
//
//
// Copyright (c) 2009 Brandon Jones
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
#if !defined(_SCRAT_CLASS_H_)
#define _SCRAT_CLASS_H_
#ifdef SQMOD_PLUGIN_API
#include <SqAPI.h>
#else
#include <squirrel.h>
#endif // SQMOD_PLUGIN_API
#include <typeinfo>
#include <squirrel.h>
#include "sqratObject.h"
#include "sqratClassType.h"
#include "sqratMemberMethods.h"
#include "sqratAllocator.h"
#include "sqratTypes.h"
namespace Sqrat
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Facilitates exposing a C++ class with no base class to Squirrel
///
/// \tparam C Class type to expose
/// \tparam A An allocator to use when instantiating and destroying class instances of this type in Squirrel
///
/// \remarks
/// DefaultAllocator is used if no allocator is specified. This should be sufficent for most classes,
/// but if specific behavior is desired, it can be overridden. If the class should not be instantiated from
/// Squirrel the NoConstructor allocator may be used. See NoCopy and CopyOnly too.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class C, class A = DefaultAllocator<C> >
class Class : public Object
{
private:
static SQInteger cleanup_hook(SQUserPointer ptr, SQInteger size) {
SQUNUSED(size);
ClassData<C>** ud = reinterpret_cast<ClassData<C>**>(ptr);
delete *ud;
return 0;
}
public:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Constructs the Class object
///
/// A Class object doesnt do anything on its own.
/// It must be told what methods and variables it contains.
/// This is done using Class methods such as Class::Func and Class::Var.
/// Then the Class must be exposed to Squirrel.
/// This is usually done by calling TableBase::Bind on a RootTable with the Class.
///
/// \param v Squirrel virtual machine to create the Class for
/// \param className A necessarily unique name for the class that can appear in error messages
/// \param createClass Should class type data be created? (almost always should be true - don't worry about it)
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Class(HSQUIRRELVM v, const string& className, bool createClass = true) : Object(v, false) {
if (createClass && !ClassType<C>::hasClassData(v)) {
sq_pushregistrytable(v);
sq_pushstring(v, "__classes", -1);
if (SQ_FAILED(sq_rawget(v, -2))) {
sq_newtable(v);
sq_pushstring(v, "__classes", -1);
sq_push(v, -2);
sq_rawset(v, -4);
}
sq_pushstring(v, className.c_str(), -1);
ClassData<C>** ud = reinterpret_cast<ClassData<C>**>(sq_newuserdata(v, sizeof(ClassData<C>*)));
*ud = new ClassData<C>;
sq_setreleasehook(v, -1, &cleanup_hook);
sq_rawset(v, -3);
sq_pop(v, 2);
ClassData<C>* cd = *ud;
if (ClassType<C>::getStaticClassData().Expired()) {
cd->staticData.Init(new StaticClassData<C, void>);
cd->staticData->copyFunc = &A::Copy;
cd->staticData->className = string(className);
cd->staticData->baseClass = NULL;
ClassType<C>::getStaticClassData() = cd->staticData;
} else {
cd->staticData = ClassType<C>::getStaticClassData().Lock();
}
HSQOBJECT& classObj = cd->classObj;
sq_resetobject(&classObj);
sq_newclass(v, false);
sq_getstackobj(v, -1, &classObj);
sq_addref(v, &classObj); // must addref before the pop!
sq_pop(v, 1);
InitClass(cd);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Gets the Squirrel object for this Class (copy)
///
/// \return Squirrel object representing the Squirrel class
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual HSQOBJECT GetObject() const {
return ClassType<C>::getClassData(vm)->classObj;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Gets the Squirrel object for this Class (reference)
///
/// \return Squirrel object representing the Squirrel class
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual HSQOBJECT& GetObject() {
return ClassType<C>::getClassData(vm)->classObj;
}
public:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Assigns a static class slot a value
///
/// \param name Name of the static slot
/// \param val Value to assign
///
/// \tparam V Type of value (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// Static values are read-only in Squirrel.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class V>
Class& SetStaticValue(const SQChar* name, const V& val) {
BindValue<V>(name, val, true);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Assigns a class slot a value
///
/// \param name Name of the slot
/// \param val Value to assign
///
/// \tparam V Type of value (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class V>
Class& SetValue(const SQChar* name, const V& val) {
BindValue<V>(name, val, false);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class variable
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param var Variable to bind
///
/// \tparam V Type of variable (usually doesnt need to be defined explicitly)
///
/// \remarks
/// If V is not a pointer or reference, then it must have a default constructor.
/// See Sqrat::Class::Prop to work around this requirement
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class V>
Class& Var(const SQChar* name, V C::* var) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
// Add the getter
BindAccessor(name, &var, sizeof(var), &sqDefaultGet<C, V>, cd->getTable);
// Add the setter
BindAccessor(name, &var, sizeof(var), &sqDefaultSet<C, V>, cd->setTable);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class variable without a setter
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param var Variable to bind
///
/// \tparam V Type of variable (usually doesnt need to be defined explicitly)
///
/// \remarks
/// If V is not a pointer or reference, then it must have a default constructor.
/// See Sqrat::Class::Prop to work around this requirement
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class V>
Class& ConstVar(const SQChar* name, V C::* var) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
// Add the getter
BindAccessor(name, &var, sizeof(var), &sqDefaultGet<C, V>, cd->getTable);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Bind a class static variable
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param var Variable to bind
///
/// \tparam V Type of variable (usually doesnt need to be defined explicitly)
///
/// \remarks
/// If V is not a pointer or reference, then it must have a default constructor.
/// See Sqrat::Class::Prop to work around this requirement
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class V>
Class& StaticVar(const SQChar* name, V* var) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
// Add the getter
BindAccessor(name, &var, sizeof(var), &sqStaticGet<C, V>, cd->getTable);
// Add the setter
BindAccessor(name, &var, sizeof(var), &sqStaticSet<C, V>, cd->setTable);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class property
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param getMethod Getter for the variable
/// \param setMethod Setter for the variable
///
/// \tparam F1 Type of get function (usually doesnt need to be defined explicitly)
/// \tparam F2 Type of set function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// This method binds setter and getter functions in C++ to Squirrel as if they are a class variable.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F1, class F2>
Class& Prop(const SQChar* name, F1 getMethod, F2 setMethod) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
if(getMethod != NULL) {
// Add the getter
BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberOverloadedFunc(getMethod), cd->getTable);
}
if(setMethod != NULL) {
// Add the setter
BindAccessor(name, &setMethod, sizeof(setMethod), SqMemberOverloadedFunc(setMethod), cd->setTable);
}
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class property (using global functions instead of member functions)
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param getMethod Getter for the variable
/// \param setMethod Setter for the variable
///
/// \tparam F1 Type of get function (usually doesnt need to be defined explicitly)
/// \tparam F2 Type of set function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// This method binds setter and getter functions in C++ to Squirrel as if they are a class variable.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F1, class F2>
Class& GlobalProp(const SQChar* name, F1 getMethod, F2 setMethod) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
if(getMethod != NULL) {
// Add the getter
BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberGlobalOverloadedFunc(getMethod), cd->getTable);
}
if(setMethod != NULL) {
// Add the setter
BindAccessor(name, &setMethod, sizeof(setMethod), SqMemberGlobalOverloadedFunc(setMethod), cd->setTable);
}
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a read-only class property
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param getMethod Getter for the variable
///
/// \tparam F Type of get function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// This method binds a getter function in C++ to Squirrel as if it is a class variable.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& Prop(const SQChar* name, F getMethod) {
// Add the getter
BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberOverloadedFunc(getMethod), ClassType<C>::getClassData(vm)->getTable);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a read-only class property (using a global function instead of a member function)
///
/// \param name Name of the variable as it will appear in Squirrel
/// \param getMethod Getter for the variable
///
/// \tparam F Type of get function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// This method binds a getter function in C++ to Squirrel as if it is a class variable.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& GlobalProp(const SQChar* name, F getMethod) {
// Add the getter
BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberGlobalOverloadedFunc(getMethod), ClassType<C>::getClassData(vm)->getTable);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class function
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& Func(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqMemberFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class function with formatting support
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& FmtFunc(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqMemberFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a class function with overloading enabled
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// Overloading in this context means to allow the function name to be used with functions
/// of a different number of arguments. This definition differs from others (e.g. C++'s).
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& Overload(const SQChar* name, F method) {
BindOverload(name, &method, sizeof(method), SqMemberOverloadedFunc(method), SqOverloadFunc(method), SqGetArgCount(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a global function as a class function
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& GlobalFunc(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqMemberGlobalFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a global function as a class function with formatting support
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& GlobalFmtFunc(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqMemberGlobalFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a static class function
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& StaticFunc(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqGlobalFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a static class function with formatting support
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& StaticFmtFunc(const SQChar* name, F method) {
BindFunc(name, &method, sizeof(method), SqGlobalFunc(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a global function as a class function with overloading enabled
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// Overloading in this context means to allow the function name to be used with functions
/// of a different number of arguments. This definition differs from others (e.g. C++'s).
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& GlobalOverload(const SQChar* name, F method) {
BindOverload(name, &method, sizeof(method), SqMemberGlobalOverloadedFunc(method), SqOverloadFunc(method), SqGetArgCount(method) - 1);
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a static class function with overloading enabled
///
/// \param name Name of the function as it will appear in Squirrel
/// \param method Function to bind
///
/// \tparam F Type of function (usually doesnt need to be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// Overloading in this context means to allow the function name to be used with functions
/// of a different number of arguments. This definition differs from others (e.g. C++'s).
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class F>
Class& StaticOverload(const SQChar* name, F method) {
BindOverload(name, &method, sizeof(method), SqGlobalOverloadedFunc(method), SqOverloadFunc(method), SqGetArgCount(method));
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a Squirrel function as defined by the Squirrel documentation as a class function
///
/// \param name Name of the function as it will appear in Squirrel
/// \param func Function to bind
///
/// \return The Class itself so the call can be chained
///
/// \remarks
/// Inside of the function, the class instance the function was called with will be at index 1 on the
/// stack and all arguments will be after that index in the order they were given to the function.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Class& SquirrelFunc(const SQChar* name, SQFUNCTION func) {
sq_pushobject(vm, ClassType<C>::getClassData(vm)->classObj);
sq_pushstring(vm, name, -1);
sq_newclosure(vm, func, 0);
sq_newslot(vm, -3, false);
sq_pop(vm, 1); // pop table
return *this;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Gets a Function from a name in the Class
///
/// \param name The name in the class that contains the Function
///
/// \return Function found in the Class (null if failed)
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Function GetFunction(const SQChar* name) {
ClassData<C>* cd = ClassType<C>::getClassData(vm);
HSQOBJECT funcObj;
sq_pushobject(vm, cd->classObj);
sq_pushstring(vm, name, -1);
#if !defined (SCRAT_NO_ERROR_CHECKING)
if(SQ_FAILED(sq_get(vm, -2))) {
sq_pop(vm, 1);
return Function();
}
SQObjectType value_type = sq_gettype(vm, -1);
if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) {
sq_pop(vm, 2);
return Function();
}
#else
sq_get(vm, -2);
#endif
sq_getstackobj(vm, -1, &funcObj);
Function ret(vm, cd->classObj, funcObj); // must addref before the pop!
sq_pop(vm, 2);
return ret;
}
protected:
/// @cond DEV
static SQInteger ClassWeakref(HSQUIRRELVM vm) {
sq_weakref(vm, -1);
return 1;
}
static SQInteger ClassTypeof(HSQUIRRELVM vm) {
sq_pushstring(vm, ClassType<C>::ClassName().c_str(), -1);
return 1;
}
static SQInteger ClassCloned(HSQUIRRELVM vm) {
SQTRY()
Sqrat::Var<const C*> other(vm, 2);
SQCATCH_NOEXCEPT(vm) {
SQCLEAR(vm);
return SQ_ERROR;
}
#if !defined (SCRAT_NO_ERROR_CHECKING)
return ClassType<C>::CopyFunc()(vm, 1, other.value);
#else
ClassType<C>::CopyFunc()(vm, 1, other.value);
return 0;
#endif
SQCATCH(vm) {
#if defined (SCRAT_USE_EXCEPTIONS)
SQUNUSED(e); // this is to avoid a warning in MSVC
#endif
return SQ_ERROR;
}
}
// Initialize the required data structure for the class
void InitClass(ClassData<C>* cd) {
cd->instances.Init(new typename unordered_map<C*, HSQOBJECT>::type);
// push the class
sq_pushobject(vm, cd->classObj);
// set the typetag of the class
sq_settypetag(vm, -1, cd->staticData.Get());
// add the default constructor
sq_pushstring(vm, _SC("constructor"), -1);
sq_newclosure(vm, &A::New, 0);
sq_newslot(vm, -3, false);
// add the set table (static)
HSQOBJECT& setTable = cd->setTable;
sq_resetobject(&setTable);
sq_pushstring(vm, _SC("__setTable"), -1);
sq_newtable(vm);
sq_getstackobj(vm, -1, &setTable);
sq_addref(vm, &setTable);
sq_newslot(vm, -3, true);
// add the get table (static)
HSQOBJECT& getTable = cd->getTable;
sq_resetobject(&getTable);
sq_pushstring(vm, _SC("__getTable"), -1);
sq_newtable(vm);
sq_getstackobj(vm, -1, &getTable);
sq_addref(vm, &getTable);
sq_newslot(vm, -3, true);
// override _set
sq_pushstring(vm, _SC("_set"), -1);
sq_pushobject(vm, setTable); // Push the set table as a free variable
sq_newclosure(vm, &sqVarSet, 1);
sq_newslot(vm, -3, false);
// override _get
sq_pushstring(vm, _SC("_get"), -1);
sq_pushobject(vm, getTable); // Push the get table as a free variable
sq_newclosure(vm, &sqVarGet, 1);
sq_newslot(vm, -3, false);
// add weakref (apparently not provided by default)
sq_pushstring(vm, _SC("weakref"), -1);
sq_newclosure(vm, &Class::ClassWeakref, 0);
sq_newslot(vm, -3, false);
// add _typeof
sq_pushstring(vm, _SC("_typeof"), -1);
sq_newclosure(vm, &Class::ClassTypeof, 0);
sq_newslot(vm, -3, false);
// add _cloned
sq_pushstring(vm, _SC("_cloned"), -1);
sq_newclosure(vm, &Class::ClassCloned, 0);
sq_newslot(vm, -3, false);
// pop the class
sq_pop(vm, 1);
}
// Helper function used to bind getters and setters
inline void BindAccessor(const SQChar* name, void* var, size_t varSize, SQFUNCTION func, HSQOBJECT table) {
// Push the get or set table
sq_pushobject(vm, table);
sq_pushstring(vm, name, -1);
// Push the variable offset as a free variable
SQUserPointer varPtr = sq_newuserdata(vm, static_cast<SQUnsignedInteger>(varSize));
memcpy(varPtr, var, varSize);
// Create the accessor function
sq_newclosure(vm, func, 1);
// Add the accessor to the table
sq_newslot(vm, -3, false);
// Pop get/set table
sq_pop(vm, 1);
}
// constructor binding
Class& BindConstructor(SQFUNCTION method, SQInteger nParams, const SQChar *name = 0) {
SQFUNCTION overload = SqOverloadFunc(method);
bool alternative_global = false;
if (name == 0)
name = _SC("constructor");
else alternative_global = true;
string overloadName = SqOverloadName::Get(name, nParams);
if (!alternative_global )
{
// push the class
sq_pushobject(vm, ClassType<C>::getClassData(vm)->classObj);
}
else
{
// the containing environment is the root table??
sq_pushroottable(vm);
}
// Bind overload handler
sq_pushstring(vm, name, -1);
sq_pushstring(vm, name, -1); // function name is passed as a free variable
sq_newclosure(vm, overload, 1);
sq_newslot(vm, -3, false);
// Bind overloaded allocator function
sq_pushstring(vm, overloadName.c_str(), -1);
sq_newclosure(vm, method, 0);
sq_setparamscheck(vm,nParams + 1,NULL);
sq_newslot(vm, -3, false);
sq_pop(vm, 1);
return *this;
}
/// @endcond
public:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with no arguments (there can only be one constructor of this many arguments for a given name)
///
/// \param name Name of the constructor as it will appear in Squirrel (default value creates a traditional constructor)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::iNew, 0, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 1 or more arguments (there can only be one constructor of this many arguments for a given name)
///
/// \param name Name of the constructor as it will appear in Squirrel (default value creates a traditional constructor)
///
/// \tparam P Type of arguments of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class... P>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<P...>, sizeof...(P), name);
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Facilitates exposing a C++ class with a base class to Squirrel
///
/// \tparam C Class type to expose
/// \tparam B Base class type (must already be bound)
/// \tparam A An allocator to use when instantiating and destroying class instances of this type in Squirrel
///
/// \remarks
/// Classes in Squirrel are single-inheritance only, and as such Sqrat only allows for single inheritance as well.
///
/// \remarks
/// DefaultAllocator is used if no allocator is specified. This should be sufficent for most classes,
/// but if specific behavior is desired, it can be overridden. If the class should not be instantiated from
/// Squirrel the NoConstructor allocator may be used. See NoCopy and CopyOnly too.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class C, class B, class A = DefaultAllocator<C> >
class DerivedClass : public Class<C, A>
{
private:
static SQInteger cleanup_hook(SQUserPointer ptr, SQInteger size) {
SQUNUSED(size);
ClassData<C>** ud = reinterpret_cast<ClassData<C>**>(ptr);
delete *ud;
return 0;
}
public:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Constructs the DerivedClass object
///
/// A DerivedClass object doesnt do anything on its own.
/// It must be told what methods and variables it contains.
/// This is done using Class methods such as Class::Func and Class::Var.
/// Then the DerivedClass must be exposed to Squirrel.
/// This is usually done by calling TableBase::Bind on a RootTable with the DerivedClass.
///
/// \param v Squirrel virtual machine to create the DerivedClass for
/// \param className A necessarily unique name for the class that can appear in error messages
///
/// \remarks
/// You MUST bind the base class fully before constructing a derived class.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DerivedClass(HSQUIRRELVM v, const string& className) : Class<C, A>(v, string(), false) {
if (!ClassType<C>::hasClassData(v)) {
sq_pushregistrytable(v);
sq_pushstring(v, "__classes", -1);
if (SQ_FAILED(sq_rawget(v, -2))) {
sq_newtable(v);
sq_pushstring(v, "__classes", -1);
sq_push(v, -2);
sq_rawset(v, -4);
}
sq_pushstring(v, className.c_str(), -1);
ClassData<C>** ud = reinterpret_cast<ClassData<C>**>(sq_newuserdata(v, sizeof(ClassData<C>*)));
*ud = new ClassData<C>;
sq_setreleasehook(v, -1, &cleanup_hook);
sq_rawset(v, -3);
sq_pop(v, 2);
ClassData<B>* bd = ClassType<B>::getClassData(v);
ClassData<C>* cd = *ud;
if (ClassType<C>::getStaticClassData().Expired()) {
cd->staticData.Init(new StaticClassData<C, B>);
cd->staticData->copyFunc = &A::Copy;
cd->staticData->className = string(className);
cd->staticData->baseClass = bd->staticData.Get();
ClassType<C>::getStaticClassData() = cd->staticData;
} else {
cd->staticData = ClassType<C>::getStaticClassData().Lock();
}
HSQOBJECT& classObj = cd->classObj;
sq_resetobject(&classObj);
sq_pushobject(v, bd->classObj);
sq_newclass(v, true);
sq_getstackobj(v, -1, &classObj);
sq_addref(v, &classObj); // must addref before the pop!
sq_pop(v, 1);
InitDerivedClass(v, cd, bd);
}
}
protected:
/// @cond DEV
void InitDerivedClass(HSQUIRRELVM vm, ClassData<C>* cd, ClassData<B>* bd) {
cd->instances.Init(new typename unordered_map<C*, HSQOBJECT>::type);
// push the class
sq_pushobject(vm, cd->classObj);
// set the typetag of the class
sq_settypetag(vm, -1, cd->staticData.Get());
// add the default constructor
sq_pushstring(vm, _SC("constructor"), -1);
sq_newclosure(vm, &A::New, 0);
sq_newslot(vm, -3, false);
// clone the base classes set table (static)
HSQOBJECT& setTable = cd->setTable;
sq_resetobject(&setTable);
sq_pushobject(vm, bd->setTable);
sq_pushstring(vm, _SC("__setTable"), -1);
sq_clone(vm, -2);
sq_remove(vm, -3);
sq_getstackobj(vm, -1, &setTable);
sq_addref(vm, &setTable);
sq_newslot(vm, -3, true);
// clone the base classes get table (static)
HSQOBJECT& getTable = cd->getTable;
sq_resetobject(&getTable);
sq_pushobject(vm, bd->getTable);
sq_pushstring(vm, _SC("__getTable"), -1);
sq_clone(vm, -2);
sq_remove(vm, -3);
sq_getstackobj(vm, -1, &getTable);
sq_addref(vm, &getTable);
sq_newslot(vm, -3, true);
// override _set
sq_pushstring(vm, _SC("_set"), -1);
sq_pushobject(vm, setTable); // Push the set table as a free variable
sq_newclosure(vm, sqVarSet, 1);
sq_newslot(vm, -3, false);
// override _get
sq_pushstring(vm, _SC("_get"), -1);
sq_pushobject(vm, getTable); // Push the get table as a free variable
sq_newclosure(vm, sqVarGet, 1);
sq_newslot(vm, -3, false);
// add weakref (apparently not provided by default)
sq_pushstring(vm, _SC("weakref"), -1);
sq_newclosure(vm, &Class<C, A>::ClassWeakref, 0);
sq_newslot(vm, -3, false);
// add _typeof
sq_pushstring(vm, _SC("_typeof"), -1);
sq_newclosure(vm, &Class<C, A>::ClassTypeof, 0);
sq_newslot(vm, -3, false);
// add _cloned
sq_pushstring(vm, _SC("_cloned"), -1);
sq_newclosure(vm, &Class<C, A>::ClassCloned, 0);
sq_newslot(vm, -3, false);
// pop the class
sq_pop(vm, 1);
}
/// @endcond
};
}
#endif