1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 16:57:16 +01:00
SqMod/include/sqrat/sqratClass.h
Sandu Liviu Catalin 0137dfc66f Move the plugin Squirrel utilities to the Sqrat binding utility.
Implement registration of functions and methods with string formatting support in the Sqrat binding utility.
Few minor other fixes.
2016-11-16 11:54:07 +02:00

1109 lines
47 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), SqMemberFuncFmt(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), SqMemberGlobalFmtFunc(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), SqGlobalFmtFunc(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 argument (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 A1 Type of argument 1 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1>, 1, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 2 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2>, 2, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 3 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3>, 3, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 4 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4>, 4, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 5 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
/// \tparam A5 Type of argument 5 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4, class A5>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4, A5>, 5, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 6 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
/// \tparam A5 Type of argument 5 of the constructor (must be defined explicitly)
/// \tparam A6 Type of argument 6 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4, class A5, class A6>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4, A5, A6>, 6, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 7 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
/// \tparam A5 Type of argument 5 of the constructor (must be defined explicitly)
/// \tparam A6 Type of argument 6 of the constructor (must be defined explicitly)
/// \tparam A7 Type of argument 7 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4, class A5, class A6, class A7>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4, A5, A6, A7>, 7, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 8 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
/// \tparam A5 Type of argument 5 of the constructor (must be defined explicitly)
/// \tparam A6 Type of argument 6 of the constructor (must be defined explicitly)
/// \tparam A7 Type of argument 7 of the constructor (must be defined explicitly)
/// \tparam A8 Type of argument 8 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4, A5, A6, A7, A8>, 8, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Binds a constructor with 9 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 A1 Type of argument 1 of the constructor (must be defined explicitly)
/// \tparam A2 Type of argument 2 of the constructor (must be defined explicitly)
/// \tparam A3 Type of argument 3 of the constructor (must be defined explicitly)
/// \tparam A4 Type of argument 4 of the constructor (must be defined explicitly)
/// \tparam A5 Type of argument 5 of the constructor (must be defined explicitly)
/// \tparam A6 Type of argument 6 of the constructor (must be defined explicitly)
/// \tparam A7 Type of argument 7 of the constructor (must be defined explicitly)
/// \tparam A8 Type of argument 8 of the constructor (must be defined explicitly)
/// \tparam A9 Type of argument 9 of the constructor (must be defined explicitly)
///
/// \return The Class itself so the call can be chained
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9>
Class& Ctor(const SQChar *name = 0) {
return BindConstructor(A::template iNew<A1, A2, A3, A4, A5, A6, A7, A8, A9>, 9, 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