Go try binding operator[] to angelscript without using a wrapper.
Frustrated yet? I thought so. The problem in this case is the preprocessor.
That is part of the motivation for a chunk of code I call the "Angelscript Class Binding Utility Templates". The rest of the motivation has to do with the macro asFUNCTION. Because asFUNCTION takes just the name of the function, C++ can't do any overload resolution. If theres more than one function with that name - as is nearly always the case with operators - it fails. Angelscript supplies the macro asFUNCTIONPR to help. But sometimes, that still isn't enough. Take the following perfectly valid C++
Yes, that is operator +=. And no, it is not a member function. You cannot bind this function without a wrapper. AngelScript expects this operator bound as a member, and thus requires either the asCALL_THISCALL or asCALL_CDECL_OBJLAST calling convention.
We've established two situations where a wrapper is absolutly neccessary. Why don't we go ahead and banish asFUNCTIONPR completely? And wouldn't it be nice if we also got C++ overload resolution and automatic conversions too? Well, we can! We can just wrap everything! Now, that's a bit of a pain in the ass. But simple templates can automate it for us.
Please note that these templates are all implicitly inline - except that we're taking their address, so they can't be inlined. However, if the actual operator they are wrapping is inline, it can be inlined into the function, making it just as effecient as if you had bound the operator directly.
/*
Angelscript Class Binding Utility Templates
Copyright (c) 2006 Anthony Casteel
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.
Anthony Casteel
jm@omnisu.com
*/
/* Important Notes --
Motivation --
Binding operators to angelscript is HARD. If there are overloads
involved, you have to specify exactly which overload you mean
using complicated macros that the preprocessor often chokes on.
For example, try binding operator[]. The preprocessor often fails
when it encounters that [] construct in the name of the function.
These templates solve that problem by invoking C++ overload
resolution system. These templates wrap the operator and make
unqualified calls to the operator, allowing the compiler to choose
the best match using argument dependant lookup and overload
resolution. All variable types are templated for maximum flexibility.
The templates do not enforce proper reference usage; you must
explicitly add & to your template parameters. No quarantees are
made as to how well this will work if you don't use cannonical
operator signatures.
AngelScript requires that some operators - such as += - be bound as
members. C++ makes no such restriction. In the case where operator+=
is declared globally, as T& operator+=(T& lhs, const T& rhs), there
is no way to directly bind it to AngelScript, as AngelScript requires
this operator to be bound with either the asCALL_THISCALL or
asCALL_CDECL_OBJLAST convention. A wrapper is neccessary in this
case. These templates can also be used to automate the generation
of such a wrapper.
If your class is not const correct, these templates will not work.
Notice that the wrappers do NOT take anything by reference. If a parameter
should be passed by reference, include the reference in the type when
instantiating the template. This decision was made because, by default,
AngelScript cannot use C++ references directly. To use references properly,
you must either use AutoHandles or turn on asUNSAFE_REFERENCES.
The constructors-with-arguments wrappers take up to only 5 arguments.
This is easily extendable.
Naming Conventions --
When a return value is appropriate, it's type is first.
Constructors take the class first, followed by the parameters.
Binary operators take the return type, the first parameter, and the
second, in that order.
Binary Operator: R operater???(F lhs, S rhs)
Binary Member operators take template parameters in the same order as
global Binary Operators.
Binary Member Operator: R S::operator???(F lhs)
Operator-> is experimentally wrapped in MemberSelection. This may or may
not work.
Some operators not supported --
() - Would require something similiar to Construct#, if supported by AS
++ - Not supported by AS (Implementations still provided)
-- - Not supported by AS (Implementations still provided)
* - Not supported by AS
& - Don't overload this. ...
(type) - Not supported by AS
, - Don't overload this.
new - AS provides alternate mechanism?
new[] - See above
delete - See above
delete[]- See above
-> - Experimental Implementation
Example Usage --
Binding a few operators for the type 'foo'
//copy constructor
engine->RegisterObjectBehaviour(
"foo",
asBEHAVE_CONSTRUCT,
"void f(const foo&)",
asFUNCTION((Wrap::Construct1<foo,foo&>)),
asCALL_CDECL_OBJLAST);
//operator+
engine->RegisterGlobalBehaviour(
asBEHAVE_ADD,
"foo f(const foo&,const foo&)",
asFUNCTION((Wrap::Add<foo,foo&,foo&>)),
asCALL_CDECL);
//operator+=
engine->RegisterObjectBehaviour(
"foo",
asBEHAVE_ADD_ASSIGN,
"foo& f(const foo&)",
asFUNCTION((Wrap::AddAssign<foo&,foo,foo&>)),
asCALL_CDECL_OBJLAST);
*/
#ifndef JM_CLASS_BINDING_UTILITY_TEMPLATES_H
#define JM_CLASS_BINDING_UTILITY_TEMPLATES_H
namespace Wrap
{
//Constructors (with up to 5 parameters)
//Use asCALL_CDECL_OBJLAST
template<typename T> void Construct(T* ptr) { new (ptr) T(); }
//Can also be used as copy constructor
template<typename T, typename P1>
void Construct1(P1 p1, T* ptr) { new (ptr) T(p1); }
template<typename T, typename P1, typename P2>
void Construct2(P1 p1, P2 p2, T* ptr) { new (ptr) T(p1,p2); }
template<typename T, typename P1, typename P2, typename P3>
void Construct3(P1 p1, P2 p2, P3 p3, T* ptr) { new (ptr) T(p1,p2,p3); }
template<typename T, typename P1, typename P2, typename P3, typename P4>
void Construct4(P1 p1, P2 p2, P3 p3, P4 p4, T* ptr) { new (ptr) T(p1,p2,p3,p4); }
template<typename T, typename P1, typename P2, typename P3, typename P4, typename P5>
void Construct5(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, T* ptr) { new (ptr) T(p1,p2,p3,p4,p5); }
//Bind as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F,typename S> R Assign(const S rhs, F* ptr) { return (*ptr) = rhs; }
template<typename F> void Destroy(F* ptr) { ptr->~F(); }
//Comparison operators.
//Bind as global behaviors; use asCALL_CDECL
template<typename F,typename S> bool Equal(const F lhs, const S rhs) { return lhs == rhs; }
template<typename F,typename S> bool NotEqual(const F lhs, const S rhs) { return lhs != rhs; }
template<typename F,typename S> bool GreaterEqual(const F lhs, const S rhs) { return lhs >= rhs; }
template<typename F,typename S> bool LessEqual(const F lhs, const S rhs) { return lhs <= rhs; }
template<typename F,typename S> bool Greater(const F lhs, const S rhs) { return lhs > rhs; }
template<typename F,typename S> bool Less(const F lhs, const S rhs) { return lhs < rhs; }
//Unary operators.
//Bind as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F> R Not(F* thisp) { return !(*thisp); }
template<typename R,typename F> R Negate(F* thisp) { return -(*thisp); }
template<typename R,typename F> R Positate(F* thisp) { return +(*thisp); }
template<typename R,typename F> R BinaryComplement(F* thisp) { return ~(*thisp); }
//Binary operators. Operator, Assign variety.
//Bind operator as global behavior; use asCALL_CDECL
//Bind assign variety as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F,typename S> R Add(const F lhs, const S rhs) { return lhs+rhs; }
template<typename R,typename F,typename S> R AddAssign(const S rhs, F* thisp) { return (*thisp) += rhs; }
template<typename R,typename F,typename S> R Subtract(const F lhs, const S rhs) { return lhs-rhs; }
template<typename R,typename F,typename S> R SubtractAssign(const S rhs, F* thisp) { return (*thisp) -= rhs; }
template<typename R,typename F,typename S> R Multiply(const F lhs, const S rhs) { return lhs*rhs; }
template<typename R,typename F,typename S> R MultiplyAssign(const S rhs, F* thisp) { return (*thisp) *= rhs; }
template<typename R,typename F,typename S> R Devide(const F lhs, const S rhs) { return lhs/rhs; }
template<typename R,typename F,typename S> R DevideAssign(const S rhs, F* thisp) { return (*thisp) /= rhs; }
template<typename R,typename F,typename S> R Modulus(const F lhs, const S rhs) { return lhs%rhs; }
template<typename R,typename F,typename S> R ModulusAssign(const S rhs, F* thisp) { return (*thisp) %= rhs; }
template<typename R,typename F,typename S> R ShiftLeft(const F lhs, const S rhs) { return lhs << rhs; }
template<typename R,typename F,typename S> R ShiftLeftAssign(const S rhs, F* thisp) { return (*thisp) <<= rhs; }
template<typename R,typename F,typename S> R ShiftRight(const F lhs, const S rhs) { return lhs >> rhs; }
template<typename R,typename F,typename S> R ShiftRightAssign(const S rhs, F* thisp) { return (*thisp) >>= rhs; }
template<typename R,typename F,typename S> R BinaryAnd(const F lhs, const S rhs) { return lhs & rhs; }
template<typename R,typename F,typename S> R BinaryAndAssign(const S rhs, F* thisp) { return (*thisp) &= rhs; }
template<typename R,typename F,typename S> R BinaryOrRight(const F lhs, const S rhs) { return lhs | rhs; }
template<typename R,typename F,typename S> R BinaryOrAssign(const S rhs, F* thisp) { return (*thisp) |= rhs; }
template<typename R,typename F,typename S> R BinaryXorRight(const F lhs, const S rhs) { return lhs ^ rhs; }
template<typename R,typename F,typename S> R BinaryXortAssign(const S rhs, F* thisp) { return (*thisp) ^= rhs; }
//These binary operators do not have an assign variety
template<typename R,typename F,typename S> R LogicalAndRight(const F lhs, const S rhs) { return lhs && rhs; }
template<typename R,typename F,typename S> R LogicalOrRight(const F lhs, const S rhs) { return lhs || rhs; }
//Index operator
//Bind as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F,typename S> R Index(const S i, F* thisp) { return (*thisp); }
//Increment operators (Not supported by AngelScript)
//Bind as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F> R PreIncrement(F* thisp) { return ++(*thisp); }
template<typename R,typename F> R PostIncrement(F* thisp) { return (*thisp)++; }
template<typename R,typename F> R PreDecrement(F* thisp) { return --(*thisp); }
template<typename R,typename F> R PostDecrement(F* thisp) { return (*thisp)--; }
//MemberSelection operator - MAY OR MAY NOT WORK.
//Bind as type behavior; use asCALL_CDECL_OBJLAST
template<typename R,typename F> R MemberSelection(F* thisp) { return thisp->operator->(); }
}
#endif