// SqPlusOverload.h // SqPlus function overloading support created by Katsuaki Kawachi. // // Const member function fixed Tegan // from http://squirrel-lang.org/forums/thread/2160.aspx #ifdef SQPLUS_OVERLOAD_RELEASE_HOOK #undef SQPLUS_OVERLOAD_RELEASE_HOOK // These end up int ClassType now static inline int destruct(SQUserPointer up, SQInteger size) { if (up) { static_cast(up)->~T(); sq_free(up, size); } return 0; } static inline SQRELEASEHOOK &release(void) { static SQRELEASEHOOK hook = ClassType::destruct; return hook; } void releaseHook(SQRELEASEHOOK releaseHook) { release() = releaseHook; //return *this; } static inline SQRELEASEHOOK &getReleaseHook(void) { return release(); } #endif // SQPLUS_OVERLOAD_RELEASE_HOOK #ifdef SQPLUS_OVERLOAD_DECLARATION #undef SQPLUS_OVERLOAD_DECLARATION template struct Arg; #endif // SQPLUS_OVERLOAD_DECLARATION #ifdef SQPLUS_OVERLOAD_IMPLEMENTATION #undef SQPLUS_OVERLOAD_IMPLEMENTATION private: class SQFuncHolder { private: template class FlexArray { protected: SquirrelObject array; public: FlexArray(int size = 0) { this->resize(size); } int size(void) const { return array.Len(); } void resize(int newSize) { if (this->size() == 0) { array = SquirrelVM::CreateArray(newSize); } else { array.ArrayResize(newSize); } } void push_back(const T &t) { this->set(this->size(), t); } void set(int index, const T &t) { get(index) = t; } T &get(int index) { if (index >= array.Len()) { resize(index + 1); } SQUserPointer up = array.GetUserPointer(index); if (!up) { up = sq_newuserdata(SquirrelVM::GetVMPtr(), sizeof(T)); new(static_cast(up)) T; array.SetUserPointer(index, up); } return *static_cast(up); } }; /* storage of wrapped C++ functions */ typedef SQInteger(*WrappedFunction)(HSQUIRRELVM, bool, int); typedef FlexArray SQWrappedFuncArray; typedef FlexArray SQWrappedFuncArray2 ; typedef FlexArray SQWrappedFuncArray3 ; struct MemberHolder { static SQWrappedFuncArray &funcs(int functionIndex, int paramCount) { static SQWrappedFuncArray3 funcs; return funcs.get(paramCount).get(functionIndex); } }; struct StaticHolder { static SQWrappedFuncArray &funcs(int functionIndex, int paramCount) { static SQWrappedFuncArray3 funcs; return funcs.get(paramCount).get(functionIndex); } }; struct ConstructorHolder { static SQWrappedFuncArray &funcs(int paramCount) { static SQWrappedFuncArray2 funcs; return funcs.get(paramCount); } }; /* wrapper for C++ functions */ template struct MemberDispatcher { static inline FlexArray &mfunc(void) { static FlexArray mfunc; return mfunc; } static inline SQInteger dispatch(HSQUIRRELVM v, bool execute, int functionIndex) { return execute ? Call(*GetInstance(v, 1), mfunc().get(functionIndex), v, 2) : Arg::argTypeDistance(v); } }; template struct StaticDispatcher { static inline FlexArray &sfunc(void) { static FlexArray sfunc; return sfunc; } static inline SQInteger dispatch(HSQUIRRELVM v, bool execute, int functionIndex) { return execute ? Call(sfunc().get(functionIndex), v, 2) : Arg::argTypeDistance(v); } }; template struct Constructor { static inline Cfunc &cfunc(void) { static Cfunc cfunc = 0; return cfunc; } static inline SQInteger construct(HSQUIRRELVM v, bool execute, int) { return execute ? Call(cfunc(), v, 2) : Arg::argTypeDistance(v); } }; // search and call an overloaded function on runtime static inline SQInteger call(SQWrappedFuncArray &funcs, HSQUIRRELVM v, int functionIndex = 0) { bool ambiguous = false; int imin = -1; int dmin = INT_MAX; for (int i = 0, size = funcs.size(); i < size; ++i) { // FIXME: to be refactored const int d = (**funcs.get(i))(v, false, functionIndex); if (d == 0) { // complete match imin = i; ambiguous = false; goto SQPLUS_OVERLOAD_CALL_IMMEDIATE_EXECUTION; } else if (0 < d && d < dmin) { dmin = d; imin = i; ambiguous = false; } else if (d == dmin) { ambiguous = true; } } if (ambiguous) { return sq_throwerror( v, _SC("Call of overloaded function is ambiguous") ); } else if (imin == -1) { return sq_throwerror( v, _SC("No match for given arguments") ); } SQPLUS_OVERLOAD_CALL_IMMEDIATE_EXECUTION: // FIXME: to be refactored return (**funcs.get(imin))(v, true, functionIndex); } public: template static inline void addMemberFunc(int functionIndex, Mfunc mfunc) { MemberHolder::funcs(functionIndex, Arg::num()).push_back( &MemberDispatcher::dispatch ); MemberDispatcher::mfunc().set(functionIndex, mfunc); } template static inline void addStaticFunc(int functionIndex, Sfunc sfunc) { StaticHolder::funcs(functionIndex, Arg::num()).push_back( &StaticDispatcher::dispatch ); StaticDispatcher::sfunc().set(functionIndex, sfunc); } template static inline void addConstructor(Cfunc cfunc) { ConstructorHolder::funcs(Arg::num()).push_back( &Constructor::construct ); Constructor::cfunc() = cfunc; } static inline SQInteger memberCall(int paramCount, HSQUIRRELVM v, int functionIndex) { return call(MemberHolder::funcs(functionIndex, paramCount), v, functionIndex); } static inline SQInteger staticCall(int paramCount, HSQUIRRELVM v, int functionIndex) { return call(StaticHolder::funcs(functionIndex, paramCount), v, functionIndex); } static inline SQInteger constructorCall(int paramCount, HSQUIRRELVM v, int) { return call(ConstructorHolder::funcs(paramCount), v); } }; // class SQFuncHolder struct FunctionNameEnumerator { SquirrelObject names; FunctionNameEnumerator(void) : names(SquirrelVM::CreateTable()) {} int index(const SQChar *n) { int i; SquirrelObject v = names.GetValue(n); if (v.IsNull()) { i = names.Len(); names.SetValue(n, i); } else { i = v.ToInteger(); } return i; } }; FunctionNameEnumerator overloadedMemberNames; FunctionNameEnumerator overloadedStaticMemberNames; static SquirrelObject & functionIndexHolders(HSQUIRRELVM v) { static SquirrelObject indexHolders; if (indexHolders.IsNull()) { sq_newtable(v); indexHolders.AttachToStackObject(-1); sq_pop(v, 1); } return indexHolders; } template static SquirrelObject & indexHolder(HSQUIRRELVM v) { static SquirrelObject holder; if (holder.IsNull()) { sq_pushobject(v, functionIndexHolders(v).GetObjectHandle()); sq_pushinteger(v, N); if (SQ_OK == sq_rawget(v, -2)) { holder.AttachToStackObject(-1); sq_pop(v, 3); } else { sq_pushinteger(v, N); sq_newtable(v); holder.AttachToStackObject(-1); sq_rawset(v, -3); sq_pop(v, 1); } } return holder; } template class SQOverloader { private: static inline SQInteger switcher(HSQUIRRELVM v, int(*caller)(int, HSQUIRRELVM, int), int fidx) { return (*caller)(StackHandler(v).GetParamCount() - 1, v, fidx); } static inline SQInteger memberSwitcher(HSQUIRRELVM v) { SQInteger fidx; sq_pushobject(v, indexHolder<0>(v).GetObjectHandle()); sq_push(v, 0); // native closure sq_rawget(v, -2); sq_getinteger(v, -1, &fidx); sq_pop(v, 2); return switcher(v, SQFuncHolder::memberCall, fidx); } static inline SQInteger staticSwitcher(HSQUIRRELVM v) { SQInteger fidx; sq_pushobject(v, indexHolder<1>(v).GetObjectHandle()); sq_push(v, 0); // native closure sq_rawget(v, -2); sq_getinteger(v, -1, &fidx); sq_pop(v, 2); return switcher(v, SQFuncHolder::staticCall, fidx); } static inline SQInteger constructorSwitcher(HSQUIRRELVM v) { return switcher(v, SQFuncHolder::constructorCall, 0); } public: static inline void addMemberFunc(SQClassDefBase *def, Func mfunc, const SQChar *name) { const int fidx = def->overloadedMemberNames.index(name); SQFuncHolder::addMemberFunc(fidx, mfunc); def->staticFuncVarArgs(memberSwitcher, name); HSQUIRRELVM v = def->v; // get closure sq_pushobject(v, def->newClass.GetObjectHandle()); sq_pushstring(v, name, -1); sq_rawget(v, -2); // holder[closure] = fidx sq_pushobject(v, indexHolder<0>(v).GetObjectHandle()); sq_push(v, -2); sq_pushinteger(v, fidx); sq_rawset(v, -3); // sq_pop(v, 3); } static inline void addOperatorFunc(SQClassDefBase *def, Func ofunc, const SQChar *name) { if (Arg::num() != 1) { //assert(false && // "Cannot add this function as operator (argc != 1)"); abort(); } SQChar proxy[256]; scsprintf(proxy, _SC("overloaded%s"), name); addMemberFunc(def, ofunc, proxy); SQChar script[512]; scsprintf(script, _SC("%s.%s<-function(o){return %s.%s(o);}"), def->name, name, def->name, proxy); SquirrelVM::RunScript(SquirrelVM::CompileBuffer(script)); } static inline void addStaticFunc(SQClassDefBase *def, Func sfunc, const SQChar *name) { const int fidx = def->overloadedStaticMemberNames.index(name); SQFuncHolder::addStaticFunc(fidx, sfunc); def->staticFuncVarArgs(staticSwitcher, name); HSQUIRRELVM v = def->v; // get closure sq_pushobject(v, def->newClass.GetObjectHandle()); sq_pushstring(v, name, -1); sq_rawget(v, -2); // holder[closure] = fidx sq_pushobject(v, indexHolder<1>(v).GetObjectHandle()); sq_push(v, -2); sq_pushinteger(v, fidx); sq_rawset(v, -3); // sq_pop(v, 3); } template static inline void addConstructor(SQClassDefBase *def, Cfunc cfunc) { SQFuncHolder::addConstructor(cfunc); def->staticFuncVarArgs(constructorSwitcher, _SC("constructor")); } static inline void addGlobalFunc(SQClassDefBase *def, Func gfunc, const SQChar *name) { const int fidx = def->overloadedStaticMemberNames.index(name); SQFuncHolder::addStaticFunc(fidx, gfunc); SquirrelVM::CreateFunctionGlobal(staticSwitcher, name, _SC("*")); HSQUIRRELVM v = def->v; // get closure sq_pushroottable(v); sq_pushstring(v, name, -1); sq_rawget(v, -2); // holder[closure] = fidx sq_pushobject(v, indexHolder<1>(v).GetObjectHandle()); sq_push(v, -2); sq_pushinteger(v, fidx); sq_rawset(v, -3); // sq_pop(v, 3); } }; public: template SQClassDefBase &overloadFunc(Mfunc mfunc, const SQChar *n) { SQOverloader::addMemberFunc(this, mfunc, n); return *this; } template SQClassDefBase &overloadOperator(Ofunc ofunc, const SQChar *n){ SQOverloader::addOperatorFunc(this, ofunc, n); return *this; } template SQClassDefBase &overloadStaticFunc(Sfunc sfunc, const SQChar *n) { SQOverloader::addStaticFunc(this, sfunc, n); return *this; } template SQClassDefBase &overloadConstructor(void) { SQOverloader::addConstructor(this, &Arg::create); return *this; } template SQClassDefBase &overloadConstructor(Cfunc cfunc) { SQOverloader::addConstructor(this, cfunc); return *this; } template SQClassDefBase &overloadGlobalFunc(Gfunc gfunc, const SQChar *n) { SQOverloader::addGlobalFunc(this, gfunc, n); return *this; } #endif // SQPLUS_OVERLOAD_IMPLEMENTATION #ifdef SQPLUS_OVERLOAD_FUNCTIONS #undef SQPLUS_OVERLOAD_FUNCTIONS struct GlobalFuncOverloader {}; static inline SQClassDefBase &globalFuncOverloader(void) { static SQClassDefBase fo(_SC("GlobalFuncOverloader")); return fo; } template void OverloadGlobal(Gfunc gfunc, const SQChar *n) { globalFuncOverloader().overloadGlobalFunc(gfunc, n); } template struct CheckInstance { template struct unref {typedef T type;}; template struct unref {typedef T type;}; template struct unref {typedef T type;}; template struct unref {typedef T type;}; template struct unref {typedef T type;}; /* d = -1 : not in hierarchy d = 0 : same d > 0 : similar (o is d-th subclass of TYPE) */ static inline int distance(HSQUIRRELVM v, int index) { HSQOBJECT o; sq_resetobject(&o); sq_getstackobj(v, index, &o); const int top = sq_gettop(v); sq_pushroottable(v); // Check plain object type int d = -1; if (Match(TypeWrapper(), v, index)) { d = 0; // Check instance type hierarchy if (sq_type(o) == OT_INSTANCE) { SQUserPointer dsttype = ClassType::type>::type(); SQUserPointer argtype; for (sq_getclass(v, index); sq_gettypetag(v, -1, &argtype) == SQ_OK; sq_getbase(v, -1)) { if (argtype == dsttype) { goto SQPLUS_OVERLOAD_DISTANCE_IMMEDIATE_RETURN; } ++d; } d = -1; // no matching type found } } SQPLUS_OVERLOAD_DISTANCE_IMMEDIATE_RETURN: sq_settop(v, top); return d; } }; template struct Arg { static inline int num(void) {return 0;} static inline int argTypeDistance(HSQUIRRELVM) { return 0; } }; template struct Arg { static inline int num(void) {return 1;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 2;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 3;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 4;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 5;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 6;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 7;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; #ifdef SQPLUS_CONST_OPT template struct Arg { static inline int num(void) {return 0;} static inline int argTypeDistance(HSQUIRRELVM) { return 0; } }; template struct Arg { static inline int num(void) {return 1;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 2;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 3;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 4;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 5;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 6;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; template struct Arg { static inline int num(void) {return 7;} static inline int argTypeDistance(HSQUIRRELVM v) { return Arg::argTypeDistance(v); } }; #endif static inline int classAllocationError(HSQUIRRELVM v) { return sq_throwerror(v, _SC("Failed to allocate memory")); } template struct Arg { static inline int num(void) {return 0;} static inline int argTypeDistance(HSQUIRRELVM) {return 0;} static inline int create(void) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R, ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 1;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 2;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 3;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 4); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2, A3 a3) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2, a3), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 4;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 4); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 5); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2, A3 a3, A4 a4) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2, a3, a4), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 5;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 4); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 5); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 6); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2, a3, a4, a5), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 6;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 4); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 5); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 6); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 7); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2, a3, a4, a5, a6), ClassType::getReleaseHook()) : classAllocationError(v); } }; template struct Arg { static inline int num(void) {return 7;} static inline int argTypeDistance(HSQUIRRELVM v) { int s, r; r = 0; s = CheckInstance::distance(v, 2); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 3); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 4); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 5); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 6); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 7); if (s < 0) {return -1;} r += s; s = CheckInstance::distance(v, 8); if (s < 0) {return -1;} r += s; return r; } static inline int create(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { HSQUIRRELVM v = SquirrelVM::GetVMPtr(); R *r = static_cast(sq_malloc(sizeof(R))); return r ? PostConstruct(v, new(r) R(a1, a2, a3, a4, a5, a6, a7), ClassType::getReleaseHook()) : classAllocationError(v); } }; #endif // SQPLUS_OVERLOAD_FUNCTIONS // SqPlusOverload.h // Local Variables: // mode: c++ // End: