Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+480
View File
@@ -0,0 +1,480 @@
#include "scriptany.h"
#include <new>
#include <assert.h>
#include <string.h>
BEGIN_AS_NAMESPACE
// We'll use the generic interface for the factories as we need the engine pointer
static void ScriptAnyFactory_Generic(asIScriptGeneric *gen)
{
asIScriptEngine *engine = gen->GetEngine();
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(engine);
}
static void ScriptAnyFactory2_Generic(asIScriptGeneric *gen)
{
asIScriptEngine *engine = gen->GetEngine();
void *ref = (void*)gen->GetArgAddress(0);
int refType = gen->GetArgTypeId(0);
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(ref,refType,engine);
}
static CScriptAny &ScriptAnyAssignment(CScriptAny *other, CScriptAny *self)
{
return *self = *other;
}
static void ScriptAnyAssignment_Generic(asIScriptGeneric *gen)
{
CScriptAny *other = (CScriptAny*)gen->GetArgObject(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*self = *other;
gen->SetReturnObject(self);
}
static void ScriptAny_Store_Generic(asIScriptGeneric *gen)
{
void *ref = (void*)gen->GetArgAddress(0);
int refTypeId = gen->GetArgTypeId(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(ref, refTypeId);
}
static void ScriptAny_StoreInt_Generic(asIScriptGeneric *gen)
{
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(*ref);
}
static void ScriptAny_StoreFlt_Generic(asIScriptGeneric *gen)
{
double *ref = (double*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(*ref);
}
static void ScriptAny_Retrieve_Generic(asIScriptGeneric *gen)
{
void *ref = (void*)gen->GetArgAddress(0);
int refTypeId = gen->GetArgTypeId(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(ref, refTypeId);
}
static void ScriptAny_RetrieveInt_Generic(asIScriptGeneric *gen)
{
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}
static void ScriptAny_RetrieveFlt_Generic(asIScriptGeneric *gen)
{
double *ref = (double*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}
static void ScriptAny_AddRef_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->AddRef();
}
static void ScriptAny_Release_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Release();
}
static void ScriptAny_GetRefCount_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}
static void ScriptAny_SetFlag_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->SetFlag();
}
static void ScriptAny_GetFlag_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
}
static void ScriptAny_EnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->EnumReferences(engine);
}
static void ScriptAny_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->ReleaseAllHandles(engine);
}
void RegisterScriptAny(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptAny_Generic(engine);
else
RegisterScriptAny_Native(engine);
}
void RegisterScriptAny_Native(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF/* | asOBJ_GC*/); assert( r >= 0 );
// We'll use the generic interface for the constructor as we need the engine pointer
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptAny,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptAny,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(?&in)", asMETHODPR(CScriptAny,Store,(void*,int),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asMETHODPR(CScriptAny,Store,(asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asMETHODPR(CScriptAny,Store,(double&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out)", asMETHODPR(CScriptAny,Retrieve,(void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out)", asMETHODPR(CScriptAny,Retrieve,(asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out)", asMETHODPR(CScriptAny,Retrieve,(double&) const,bool), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours
//r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptAny,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
//r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptAny,SetFlag), asCALL_THISCALL); assert( r >= 0 );
//r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptAny,GetFlag), asCALL_THISCALL); assert( r >= 0 );
//r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptAny,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
//r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptAny,ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
}
void RegisterScriptAny_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
// We'll use the generic interface for the constructor as we need the engine pointer
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptAny_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptAny_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(?&in)", asFUNCTION(ScriptAny_Store_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asFUNCTION(ScriptAny_StoreInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asFUNCTION(ScriptAny_StoreFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out) const", asFUNCTION(ScriptAny_Retrieve_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out) const", asFUNCTION(ScriptAny_RetrieveInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out) const", asFUNCTION(ScriptAny_RetrieveFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptAny_GetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptAny_SetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptAny_GetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptAny_EnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptAny_ReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 );
}
CScriptAny &CScriptAny::operator=(const CScriptAny &other)
{
// Hold on to the object type reference so it isn't destroyed too early
if( other.value.typeId & asTYPEID_MASK_OBJECT )
{
asITypeInfo *ti = engine->GetTypeInfoById(other.value.typeId);
if( ti )
ti->AddRef();
}
FreeObject();
value.typeId = other.value.typeId;
if( value.typeId & asTYPEID_OBJHANDLE )
{
// For handles, copy the pointer and increment the reference count
value.valueObj = other.value.valueObj;
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
value.valueObj = engine->CreateScriptObjectCopy(other.value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else
{
// Primitives can be copied directly
value.valueInt = other.value.valueInt;
}
return *this;
}
int CScriptAny::CopyFrom(const CScriptAny *other)
{
if( other == 0 ) return asINVALID_ARG;
*this = *(CScriptAny*)other;
return 0;
}
CScriptAny::CScriptAny(asIScriptEngine *engine)
{
this->engine = engine;
refCount = 1;
gcFlag = false;
value.typeId = 0;
value.valueInt = 0;
// Notify the garbage collector of this object
//engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
}
CScriptAny::CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine)
{
this->engine = engine;
refCount = 1;
gcFlag = false;
value.typeId = 0;
value.valueInt = 0;
// Notify the garbage collector of this object
//engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
Store(ref, refTypeId);
}
CScriptAny::~CScriptAny()
{
FreeObject();
}
void CScriptAny::Store(void *ref, int refTypeId)
{
// This method is not expected to be used for primitive types, except for bool, int64, or double
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_VOID || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
// Hold on to the object type reference so it isn't destroyed too early
if( refTypeId & asTYPEID_MASK_OBJECT )
{
asITypeInfo *ti = engine->GetTypeInfoById(refTypeId);
if( ti )
ti->AddRef();
}
FreeObject();
value.typeId = refTypeId;
if( value.typeId & asTYPEID_OBJHANDLE )
{
// We're receiving a reference to the handle, so we need to dereference it
value.valueObj = *(void**)ref;
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
value.valueObj = engine->CreateScriptObjectCopy(ref, engine->GetTypeInfoById(value.typeId));
}
else
{
// Primitives can be copied directly
value.valueInt = 0;
// Copy the primitive value
// We receive a pointer to the value.
int size = engine->GetSizeOfPrimitiveType(value.typeId);
memcpy(&value.valueInt, ref, size);
}
}
void CScriptAny::Store(double &ref)
{
Store(&ref, asTYPEID_DOUBLE);
}
void CScriptAny::Store(asINT64 &ref)
{
Store(&ref, asTYPEID_INT64);
}
bool CScriptAny::Retrieve(void *ref, int refTypeId) const
{
// This method is not expected to be used for primitive types, except for bool, int64, or double
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
if( refTypeId & asTYPEID_OBJHANDLE )
{
// Is the handle type compatible with the stored value?
// A handle can be retrieved if the stored type is a handle of same or compatible type
// or if the stored type is an object that implements the interface that the handle refer to.
if( (value.typeId & asTYPEID_MASK_OBJECT) )
{
// Don't allow the retrieval if the stored handle is to a const object but not the wanted handle
if( (value.typeId & asTYPEID_HANDLETOCONST) && !(refTypeId & asTYPEID_HANDLETOCONST) )
return false;
// RefCastObject will increment the refCount of the returned pointer if successful
engine->RefCastObject(value.valueObj, engine->GetTypeInfoById(value.typeId), engine->GetTypeInfoById(refTypeId), reinterpret_cast<void**>(ref));
if( *(asPWORD*)ref == 0 )
return false;
return true;
}
}
else if( refTypeId & asTYPEID_MASK_OBJECT )
{
// Is the object type compatible with the stored value?
// Copy the object into the given reference
if( value.typeId == refTypeId )
{
engine->AssignScriptObject(ref, value.valueObj, engine->GetTypeInfoById(value.typeId));
return true;
}
}
else
{
// Is the primitive type compatible with the stored value?
if( value.typeId == refTypeId )
{
int size = engine->GetSizeOfPrimitiveType(refTypeId);
memcpy(ref, &value.valueInt, size);
return true;
}
// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
if( value.typeId == asTYPEID_INT64 && refTypeId == asTYPEID_DOUBLE )
{
*(double*)ref = double(value.valueInt);
return true;
}
else if( value.typeId == asTYPEID_DOUBLE && refTypeId == asTYPEID_INT64 )
{
*(asINT64*)ref = asINT64(value.valueFlt);
return true;
}
}
return false;
}
bool CScriptAny::Retrieve(asINT64 &outValue) const
{
return Retrieve(&outValue, asTYPEID_INT64);
}
bool CScriptAny::Retrieve(double &outValue) const
{
return Retrieve(&outValue, asTYPEID_DOUBLE);
}
int CScriptAny::GetTypeId() const
{
return value.typeId;
}
void CScriptAny::FreeObject()
{
// If it is a handle or a ref counted object, call release
if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Let the engine release the object
asITypeInfo *ti = engine->GetTypeInfoById(value.typeId);
engine->ReleaseScriptObject(value.valueObj, ti);
// Release the object type info
if( ti )
ti->Release();
value.valueObj = 0;
value.typeId = 0;
}
// For primitives, there's nothing to do
}
void CScriptAny::EnumReferences(asIScriptEngine *inEngine)
{
// If we're holding a reference, we'll notify the garbage collector of it
if( value.valueObj && (value.typeId & asTYPEID_MASK_OBJECT) )
{
inEngine->GCEnumCallback(value.valueObj);
// The object type itself is also garbage collected
asITypeInfo *ti = inEngine->GetTypeInfoById(value.typeId);
if( ti )
inEngine->GCEnumCallback(ti);
}
}
void CScriptAny::ReleaseAllHandles(asIScriptEngine * /*engine*/)
{
FreeObject();
}
int CScriptAny::AddRef() const
{
// Increase counter and clear flag set by GC
gcFlag = false;
return asAtomicInc(refCount);
}
int CScriptAny::Release() const
{
// Decrease the ref counter
gcFlag = false;
if( asAtomicDec(refCount) == 0 )
{
// Delete this object as no more references to it exists
delete this;
return 0;
}
return refCount;
}
int CScriptAny::GetRefCount()
{
return refCount;
}
void CScriptAny::SetFlag()
{
gcFlag = true;
}
bool CScriptAny::GetFlag()
{
return gcFlag;
}
END_AS_NAMESPACE
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,771 @@
#include <assert.h>
#include <string.h>
#include "scriptdictionary.h"
#include "scriptarray.h"
#include "../source/as_scriptengine.h"
#include "../source/as_scriptobject.h"
BEGIN_AS_NAMESPACE
using namespace std;
bool IsHandleCompatibleWithObject(asCScriptEngine* engine, void *obj, int objTypeId, int handleTypeId)
{
// if equal, then it is obvious they are compatible
if( objTypeId == handleTypeId )
return true;
// Get the actual data types from the type ids
asCDataType objDt = engine->GetDataTypeFromTypeId(objTypeId);
asCDataType hdlDt = engine->GetDataTypeFromTypeId(handleTypeId);
// A handle to const cannot be passed to a handle that is not referencing a const object
if( objDt.IsHandleToConst() && !hdlDt.IsHandleToConst() )
return false;
if( objDt.GetTypeInfo() == hdlDt.GetTypeInfo() )
{
// The object type is equal
return true;
}
else if( objDt.IsScriptObject() && obj )
{
// Get the true type from the object instance
asITypeInfo *objType = ((asCScriptObject*)obj)->GetObjectType();
// Check if the object implements the interface, or derives from the base class
// This will also return true, if the requested handle type is an exact match for the object type
if( objType->Implements(hdlDt.GetTypeInfo()) ||
objType->DerivesFrom(hdlDt.GetTypeInfo()) )
return true;
}
return false;
}
//--------------------------------------------------------------------------
// CScriptDictionary implementation
CScriptDictionary::CScriptDictionary(asIScriptEngine *engine)
{
// We start with one reference
refCount.set(1);
gcFlag = false;
// Start at the zeroth modification
modCount = 0;
// Keep a reference to the engine for as long as we live
// We don't increment the reference counter, because the
// engine will hold a pointer to the object.
this->engine = engine;
// Notify the garbage collector of this object
// TODO: The type id should be cached
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("dictionary"));
}
CScriptDictionary::~CScriptDictionary()
{
// Delete all keys and values
DeleteAll();
}
void CScriptDictionary::AddRef() const
{
// We need to clear the GC flag
gcFlag = false;
refCount.atomicInc();
}
void CScriptDictionary::Release() const
{
// We need to clear the GC flag
gcFlag = false;
if( refCount.atomicDec() == 0 )
delete this;
}
int CScriptDictionary::GetRefCount()
{
return refCount.get();
}
void CScriptDictionary::SetGCFlag()
{
gcFlag = true;
}
bool CScriptDictionary::GetGCFlag()
{
return gcFlag;
}
void CScriptDictionary::EnumReferences(asIScriptEngine *engine)
{
// Call the gc enum callback for each of the objects
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_MASK_OBJECT )
engine->GCEnumCallback(it->second.valueObj);
}
}
void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/)
{
// We're being told to release all references in
// order to break circular references for dead objects
DeleteAll();
}
CScriptDictionary &CScriptDictionary::operator =(const CScriptDictionary &other)
{
// Clear everything we had before
DeleteAll();
// Do a shallow copy of the dictionary
mapType::const_iterator it;
for( it = other.dict.begin(); it != other.dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_OBJHANDLE )
Set(it->first, (void*)&it->second.valueObj, it->second.typeId);
else if( it->second.typeId & asTYPEID_MASK_OBJECT )
Set(it->first, (void*)it->second.valueObj, it->second.typeId);
else
Set(it->first, (void*)&it->second.valueInt, it->second.typeId);
}
return *this;
}
void CScriptDictionary::Set(const string &key, void *value, int typeId)
{
valueStruct valStruct = {{0},0};
valStruct.typeId = typeId;
if( typeId & asTYPEID_OBJHANDLE )
{
// We're receiving a reference to the handle, so we need to dereference it
valStruct.valueObj = *(void**)value;
engine->AddRefScriptObject(valStruct.valueObj, engine->GetTypeInfoById(typeId));
}
else if( typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
valStruct.valueObj = engine->CreateScriptObjectCopy(value, engine->GetTypeInfoById(typeId));
}
else
{
// Copy the primitive value
// We receive a pointer to the value.
int size = engine->GetSizeOfPrimitiveType(typeId);
memcpy(&valStruct.valueInt, value, size);
}
mapType::iterator it;
it = dict.find(key);
if( it != dict.end() )
{
FreeValue(it->second);
// Insert the new value
it->second = valStruct;
}
else
{
dict.insert(mapType::value_type(key, valStruct));
++modCount;
}
}
// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the dictionary as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a
// different type.
void CScriptDictionary::Set(const string &key, asINT64 &value)
{
Set(key, &value, asTYPEID_INT64);
}
// This overloaded method is implemented so that all floating point types
// will be stored in the dictionary as double through implicit conversions.
// This simplifies the management of the numeric types when the script
// retrieves the stored value using a different type.
void CScriptDictionary::Set(const string &key, double &value)
{
Set(key, &value, asTYPEID_DOUBLE);
}
// This helper function exists to assist various dictionary and iterator
// methods that want to retrieve a value from a standard iterator
bool CScriptDictionary::Iterator_GetValue(CScriptDictionary::mapType::const_iterator &it, void *value, int typeId) const {
// Return the value
if( typeId & asTYPEID_OBJHANDLE )
{
// A handle can be retrieved if the stored type is a handle of same or compatible type
// or if the stored type is an object that implements the interface that the handle refer to.
if( (it->second.typeId & asTYPEID_MASK_OBJECT) &&
IsHandleCompatibleWithObject((asCScriptEngine*)engine, it->second.valueObj, it->second.typeId, typeId) )
{
engine->AddRefScriptObject(it->second.valueObj, engine->GetTypeInfoById(it->second.typeId));
*(void**)value = it->second.valueObj;
return true;
}
}
else if( typeId & asTYPEID_MASK_OBJECT )
{
// Verify that the copy can be made
bool isCompatible = false;
if( it->second.typeId == typeId )
isCompatible = true;
// Copy the object into the given reference
if( isCompatible )
{
engine->AssignScriptObject(value, it->second.valueObj, engine->GetTypeInfoById(typeId));
return true;
}
}
else
{
if( it->second.typeId == typeId )
{
int size = engine->GetSizeOfPrimitiveType(typeId);
memcpy(value, &it->second.valueInt, size);
return true;
}
// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
if( it->second.typeId == asTYPEID_INT64 && typeId == asTYPEID_DOUBLE )
{
*(double*)value = double(it->second.valueInt);
return true;
}
else if( it->second.typeId == asTYPEID_DOUBLE && typeId == asTYPEID_INT64 )
{
*(asINT64*)value = asINT64(it->second.valueFlt);
return true;
}
}
// AngelScript has already initialized the value with a default value,
// so we don't have to do anything if we don't find the element, or if
// the element is incompatible with the requested type.
return false;
}
// Returns true if the value was successfully retrieved
bool CScriptDictionary::Get(const string &key, void *value, int typeId) const
{
mapType::const_iterator it;
it = dict.find(key);
if( it != dict.end() )
{
// This logic is already implemented in the iterator wrapper,
// so defer to that function from here
return Iterator_GetValue(it, value, typeId);
}
return false;
}
bool CScriptDictionary::Get(const string &key, asINT64 &value) const
{
return Get(key, &value, asTYPEID_INT64);
}
bool CScriptDictionary::Get(const string &key, double &value) const
{
return Get(key, &value, asTYPEID_DOUBLE);
}
bool CScriptDictionary::Exists(const string &key) const
{
mapType::const_iterator it;
it = dict.find(key);
if( it != dict.end() )
return true;
return false;
}
bool CScriptDictionary::IsEmpty() const
{
if( dict.size() == 0 )
return true;
return false;
}
asUINT CScriptDictionary::GetSize() const
{
return asUINT(dict.size());
}
void CScriptDictionary::Delete(const string &key)
{
mapType::iterator it;
it = dict.find(key);
if( it != dict.end() )
{
FreeValue(it->second);
dict.erase(it);
++modCount;
}
}
void CScriptDictionary::DeleteAll()
{
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
FreeValue(it->second);
dict.clear();
}
void CScriptDictionary::DeleteNulls()
{
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_OBJHANDLE )
if( it->second.valueObj == 0 )
it = dict.erase(it);
}
++modCount;
}
void CScriptDictionary::FreeValue(valueStruct &value)
{
// If it is a handle or a ref counted object, call release
if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Let the engine release the object
engine->ReleaseScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
value.valueObj = 0;
value.typeId = 0;
}
// For primitives, there's nothing to do
}
CScriptArray* CScriptDictionary::GetKeys() const
{
// TODO: optimize: The string array type should only be determined once.
// It should be recomputed when registering the dictionary class.
// Only problem is if multiple engines are used, as they may not
// share the same type id. Alternatively it can be stored in the
// user data for the dictionary type.
int stringArrayType = engine->GetTypeIdByDecl("array<string>");
asITypeInfo *ot = engine->GetTypeInfoById(stringArrayType);
// Create the array object
CScriptArray *array = new CScriptArray(dict.size(), ot);
long current = -1;
mapType::const_iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
current++;
*(string*)array->At(current) = it->first;
}
return array;
}
CScriptDictionary::Iterator CScriptDictionary::GetIterator() const {
return Iterator(this);
}
void CScriptDictionary::Delete(CScriptDictionary::Iterator& it) {
if( it.container != this ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Mismatched script container and iterator.");
return;
}
if( it.it == dict.end() ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Deleting invalid iterator.");
return;
}
it.it = dict.erase(it.it);
it.first = true;
++modCount;
it.modCount = modCount;
}
//--------------------------------------------------------------------------
// Iterator implementation
static const std::string errorString = "(error)";
CScriptDictionary::Iterator::Iterator() {
container = 0;
}
CScriptDictionary::Iterator::Iterator(const CScriptDictionary *container) {
this->container = container;
// Start the iteration
it = container->dict.begin();
first = true;
// Track the modification count of the dictionary
// so we can safely fail when the dictionary is altered
modCount = container->modCount;
// Keep a reference to the dictionary so it doesn't
// get destroyed from under us
container->AddRef();
}
CScriptDictionary::Iterator::~Iterator() {
// Release dictionary reference
if(container)
container->Release();
}
static void DefaultConstructIterator(void *memory) {
new(memory) CScriptDictionary::Iterator();
}
static void ConstructIterator(void *memory, const CScriptDictionary *container) {
new(memory) CScriptDictionary::Iterator(container);
}
static void DestructIterator(CScriptDictionary::Iterator &it) {
it.~Iterator();
}
const std::string &CScriptDictionary::Iterator::GetKey() {
if( !container || modCount != container->modCount || it == container->dict.end() ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Iterator not valid or dictionary changed.");
return errorString;
}
return it->first;
}
bool CScriptDictionary::Iterator::Iterate(void *value, int typeId) {
return Iterate(0, value, typeId);
}
bool CScriptDictionary::Iterator::Iterate(string* key, void *value, int typeId) {
if( !container ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Iterating on invalid iterator.");
return false;
}
if( modCount != container->modCount ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Dictionary changed during iteration.");
return false;
}
if( it == container->dict.end() )
return false;
if( first )
first = false;
else
++it;
if( it == container->dict.end() )
return false;
if( key )
*key = it->first;
if( container->Iterator_GetValue(it, value, typeId) )
{
return true;
}
else
{
// The caller can check IsValid to see if the iteration can still
// continue because the type was wrong, or whether it has really ended
return false;
}
}
bool CScriptDictionary::Iterator::IsValid() {
return container && modCount == container->modCount && it != container->dict.end();
}
CScriptDictionary::Iterator &CScriptDictionary::Iterator::operator =(const CScriptDictionary::Iterator &other) {
if(other.container)
other.container->AddRef();
if(container)
container->Release();
modCount = other.modCount;
container = other.container;
it = other.it;
first = other.first;
return *this;
}
//--------------------------------------------------------------------------
// Generic wrappers
void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen)
{
*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = new CScriptDictionary(gen->GetEngine());
}
void ScriptDictionaryAddRef_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
dict->AddRef();
}
void ScriptDictionaryRelease_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
dict->Release();
}
void ScriptDictionaryAssign_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
CScriptDictionary *other = *(CScriptDictionary**)gen->GetAddressOfArg(0);
*dict = *other;
*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = dict;
}
void ScriptDictionarySet_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
int typeId = gen->GetArgTypeId(1);
dict->Set(*key, ref, typeId);
}
void ScriptDictionarySetInt_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
dict->Set(*key, *(asINT64*)ref);
}
void ScriptDictionarySetFlt_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
dict->Set(*key, *(double*)ref);
}
void ScriptDictionaryGet_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
int typeId = gen->GetArgTypeId(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, typeId);
}
void ScriptDictionaryGetInt_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(asINT64*)ref);
}
void ScriptDictionaryGetFlt_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(double*)ref);
}
void ScriptDictionaryExists_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
bool ret = dict->Exists(*key);
*(bool*)gen->GetAddressOfReturnLocation() = ret;
}
void ScriptDictionaryDelete_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
string *key = *(string**)gen->GetAddressOfArg(0);
dict->Delete(*key);
}
void ScriptDictionaryDeleteAll_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
dict->DeleteAll();
}
static void ScriptDictionaryGetRefCount_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}
static void ScriptDictionarySetGCFlag_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
self->SetGCFlag();
}
static void ScriptDictionaryGetGCFlag_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag();
}
static void ScriptDictionaryEnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->EnumReferences(engine);
}
static void ScriptDictionaryReleaseAllReferences_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->ReleaseAllReferences(engine);
}
static void CScriptDictionaryGetKeys_Generic(asIScriptGeneric *gen)
{
CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
*(CScriptArray**)gen->GetAddressOfReturnLocation() = self->GetKeys();
}
//--------------------------------------------------------------------------
// Register the type
void RegisterScriptDictionary(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptDictionary_Generic(engine);
else
RegisterScriptDictionary_Native(engine);
}
void RegisterScriptDictionary_Native(asIScriptEngine *engine)
{
int r;
//Register iterator type so we can use it later
r = engine->RegisterObjectType("dictionary_iterator", sizeof(CScriptDictionary::Iterator), asOBJ_VALUE | asOBJ_APP_CLASS_CDA); assert( r >= 0 );
r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
// Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asMETHODPR(CScriptDictionary, operator=, (const CScriptDictionary &), CScriptDictionary&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const ?&in)", asMETHODPR(CScriptDictionary,Set,(const string&,void*,int),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asMETHODPR(CScriptDictionary,Set,(const string&,asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asMETHODPR(CScriptDictionary,Set,(const string&,double&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,double&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asMETHOD(CScriptDictionary,Exists), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool isEmpty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "uint getSize() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asMETHODPR(CScriptDictionary, Delete, (const string&), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void delete(dictionary_iterator&)", asMETHODPR(CScriptDictionary, Delete, (CScriptDictionary::Iterator&), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void deleteNulls()", asMETHOD(CScriptDictionary,DeleteNulls), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "dictionary_iterator iterator()", asMETHOD(CScriptDictionary,GetIterator), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asMETHOD(CScriptDictionary,GetKeys), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptDictionary,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptDictionary,SetGCFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptDictionary,GetGCFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 );
#if AS_USE_STLNAMES == 1
// Same as isEmpty
r = engine->RegisterObjectMethod("dictionary", "bool empty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
// Same as getSize
r = engine->RegisterObjectMethod("dictionary", "uint size() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
// Same as delete
r = engine->RegisterObjectMethod("dictionary", "void erase(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 );
// Same as deleteAll
r = engine->RegisterObjectMethod("dictionary", "void clear()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
#endif
// Register iterator methods
r = engine->RegisterObjectBehaviour("dictionary_iterator", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(DefaultConstructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("dictionary_iterator", asBEHAVE_CONSTRUCT, "void f(const dictionary &in)", asFUNCTION(ConstructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("dictionary_iterator", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectMethod("dictionary_iterator", "const string& get_key()", asMETHOD(CScriptDictionary::Iterator, GetKey), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("dictionary_iterator", "bool iterate(?&out)", asMETHODPR(CScriptDictionary::Iterator, Iterate, (void*,int), bool), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("dictionary_iterator", "bool iterate(string &out, ?&out)", asMETHODPR(CScriptDictionary::Iterator, Iterate, (string*,void*,int), bool), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("dictionary_iterator", "bool get_valid()", asMETHOD(CScriptDictionary::Iterator, IsValid), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("dictionary_iterator", "dictionary_iterator& opAssign(const dictionary_iterator &in)", asMETHOD(CScriptDictionary::Iterator, operator=), asCALL_THISCALL); assert( r>= 0 );
}
void RegisterScriptDictionary_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictionaryAddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictionaryRelease_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asFUNCTION(ScriptDictionaryAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asFUNCTION(ScriptDictionarySetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptDictionaryGetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asFUNCTION(ScriptDictionarySetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asFUNCTION(ScriptDictionaryGetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asFUNCTION(ScriptDictionaryExists_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asFUNCTION(ScriptDictionaryDelete_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asFUNCTION(ScriptDictionaryDeleteAll_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asFUNCTION(CScriptDictionaryGetKeys_Generic), asCALL_GENERIC); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptDictionaryGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptDictionarySetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptDictionaryGetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
}
END_AS_NAMESPACE
+329
View File
@@ -0,0 +1,329 @@
#include "scripthandle.h"
#include <new>
#include <assert.h>
#include <string.h>
BEGIN_AS_NAMESPACE
static void Construct(CScriptHandle *self) { new(self) CScriptHandle(); }
static void Construct(CScriptHandle *self, const CScriptHandle &o) { new(self) CScriptHandle(o); }
// This one is not static because it needs to be friend with the CScriptHandle class
void Construct(CScriptHandle *self, void *ref, int typeId) { new(self) CScriptHandle(ref, typeId); }
static void Destruct(CScriptHandle *self) { self->~CScriptHandle(); }
CScriptHandle::CScriptHandle()
{
m_ref = 0;
m_type = 0;
}
CScriptHandle::CScriptHandle(const CScriptHandle &other)
{
m_ref = other.m_ref;
m_type = other.m_type;
AddRefHandle();
}
CScriptHandle::CScriptHandle(void *ref, asITypeInfo *type)
{
m_ref = ref;
m_type = type;
AddRefHandle();
}
// This constructor shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle::CScriptHandle(void *ref, int typeId)
{
m_ref = 0;
m_type = 0;
Assign(ref, typeId);
}
CScriptHandle::~CScriptHandle()
{
ReleaseHandle();
}
void CScriptHandle::ReleaseHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->ReleaseScriptObject(m_ref, m_type);
engine->Release();
m_ref = 0;
m_type = 0;
}
}
void CScriptHandle::AddRefHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->AddRefScriptObject(m_ref, m_type);
// Hold on to the engine so it isn't destroyed while
// a reference to a script object is still held
engine->AddRef();
}
}
CScriptHandle &CScriptHandle::operator =(const CScriptHandle &other)
{
Set(other.m_ref, other.m_type);
return *this;
}
void CScriptHandle::Set(void *ref, asITypeInfo *type)
{
if( m_ref == ref ) return;
ReleaseHandle();
m_ref = ref;
m_type = type;
AddRefHandle();
}
void *CScriptHandle::GetRef()
{
return m_ref;
}
asITypeInfo *CScriptHandle::GetType() const
{
return m_type;
}
int CScriptHandle::GetTypeId() const
{
if( m_type == 0 ) return 0;
return m_type->GetTypeId() | asTYPEID_OBJHANDLE;
}
// This method shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle &CScriptHandle::Assign(void *ref, int typeId)
{
// When receiving a null handle we just clear our memory
if( typeId == 0 )
{
Set(0, 0);
return *this;
}
// Dereference received handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Store the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// Get the object type
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
// If the argument is another CScriptHandle, we should copy the content instead
if( type && strcmp(type->GetName(), "ref") == 0 )
{
CScriptHandle *r = (CScriptHandle*)ref;
ref = r->m_ref;
type = r->m_type;
}
Set(ref, type);
return *this;
}
bool CScriptHandle::operator==(const CScriptHandle &o) const
{
if( m_ref == o.m_ref &&
m_type == o.m_type )
return true;
// TODO: If type is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
return false;
}
bool CScriptHandle::operator!=(const CScriptHandle &o) const
{
return !(*this == o);
}
bool CScriptHandle::Equals(void *ref, int typeId) const
{
// Null handles are received as reference to a null handle
if( typeId == 0 )
ref = 0;
// Dereference handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Compare the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// TODO: If typeId is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
if( ref == m_ref ) return true;
return false;
}
// AngelScript: used as '@obj = cast<obj>(ref);'
void CScriptHandle::Cast(void **outRef, int typeId)
{
// If we hold a null handle, then just return null
if( m_type == 0 )
{
*outRef = 0;
return;
}
// It is expected that the outRef is always a handle
assert( typeId & asTYPEID_OBJHANDLE );
// Compare the type id of the actual object
typeId &= ~asTYPEID_OBJHANDLE;
asIScriptEngine *engine = m_type->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
*outRef = 0;
// RefCastObject will increment the refCount of the returned object if successful
engine->RefCastObject(m_ref, m_type, type, outRef);
}
void RegisterScriptHandle_Native(asIScriptEngine *engine)
{
int r;
#if AS_CAN_USE_CPP11
// With C++11 it is possible to use asGetTypeTraits to automatically determine the flags that represent the C++ class
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asGetTypeTraits<CScriptHandle>()); assert( r >= 0 );
#else
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
#endif
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR(Construct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTIONPR(Construct, (CScriptHandle *, const CScriptHandle &), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTIONPR(Construct, (CScriptHandle *, void *, int), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTIONPR(Destruct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asMETHODPR(CScriptHandle, Cast, (void **, int), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asMETHOD(CScriptHandle, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asMETHOD(CScriptHandle, Assign), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asMETHODPR(CScriptHandle, operator==, (const CScriptHandle &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asMETHODPR(CScriptHandle, Equals, (void*, int) const, bool), asCALL_THISCALL); assert( r >= 0 );
}
void CScriptHandle_Construct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle();
}
void CScriptHandle_ConstructCopy_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle(*other);
}
void CScriptHandle_ConstructVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
Construct(self, ref, typeId);
}
void CScriptHandle_Destruct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->~CScriptHandle();
}
void CScriptHandle_Cast_Generic(asIScriptGeneric *gen)
{
void **ref = reinterpret_cast<void**>(gen->GetArgAddress(0));
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Cast(ref, typeId);
}
void CScriptHandle_Assign_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
*self = *other;
gen->SetReturnAddress(self);
}
void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Assign(ref, typeId);
gen->SetReturnAddress(self);
}
void CScriptHandle_Equals_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(*self == *other);
}
void CScriptHandle_EqualsVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(self->Equals(ref, typeId));
}
void RegisterScriptHandle_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptHandle_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTION(CScriptHandle_ConstructCopy_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTION(CScriptHandle_ConstructVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptHandle_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asFUNCTION(CScriptHandle_Cast_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asFUNCTION(CScriptHandle_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asFUNCTION(CScriptHandle_AssignVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asFUNCTION(CScriptHandle_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asFUNCTION(CScriptHandle_EqualsVar_Generic), asCALL_GENERIC); assert( r >= 0 );
}
void RegisterScriptHandle(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptHandle_Generic(engine);
else
RegisterScriptHandle_Native(engine);
}
END_AS_NAMESPACE
+937
View File
@@ -0,0 +1,937 @@
#include <string.h>
#include "scripthelper.h"
#include <assert.h>
#include <stdio.h>
#include <fstream>
#include <set>
#include <stdlib.h>
using namespace std;
BEGIN_AS_NAMESPACE
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result)
{
// TODO: If a lot of script objects are going to be compared, e.g. when sorting an array,
// then the method id and context should be cached between calls.
int retval = -1;
asIScriptFunction *func = 0;
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
if( ti )
{
// Check if the object type has a compatible opCmp method
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
{
asIScriptFunction *f = ti->GetMethodByIndex(n);
asDWORD flags;
if( strcmp(f->GetName(), "opCmp") == 0 &&
f->GetReturnTypeId(&flags) == asTYPEID_INT32 &&
flags == asTM_NONE &&
f->GetParamCount() == 1 )
{
int paramTypeId;
f->GetParam(0, &paramTypeId, &flags);
// The parameter must be an input reference of the same type
// If the reference is a inout reference, then it must also be read-only
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
break;
// Found the method
func = f;
break;
}
}
}
if( func )
{
// Call the method
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
ctx->SetObject(lobj);
ctx->SetArgAddress(0, robj);
int r = ctx->Execute();
if( r == asEXECUTION_FINISHED )
{
result = (int)ctx->GetReturnDWord();
// The comparison was successful
retval = 0;
}
ctx->Release();
}
return retval;
}
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result)
{
// TODO: If a lot of script objects are going to be compared, e.g. when searching for an
// entry in a set, then the method and context should be cached between calls.
int retval = -1;
asIScriptFunction *func = 0;
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
if( ti )
{
// Check if the object type has a compatible opEquals method
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
{
asIScriptFunction *f = ti->GetMethodByIndex(n);
asDWORD flags;
if( strcmp(f->GetName(), "opEquals") == 0 &&
f->GetReturnTypeId(&flags) == asTYPEID_BOOL &&
flags == asTM_NONE &&
f->GetParamCount() == 1 )
{
int paramTypeId;
f->GetParam(0, &paramTypeId, &flags);
// The parameter must be an input reference of the same type
// If the reference is a inout reference, then it must also be read-only
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
break;
// Found the method
func = f;
break;
}
}
}
if( func )
{
// Call the method
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
ctx->SetObject(lobj);
ctx->SetArgAddress(0, robj);
int r = ctx->Execute();
if( r == asEXECUTION_FINISHED )
{
result = ctx->GetReturnByte() ? true : false;
// The comparison was successful
retval = 0;
}
ctx->Release();
}
else
{
// If the opEquals method doesn't exist, then we try with opCmp instead
int relation;
retval = CompareRelation(engine, lobj, robj, typeId, relation);
if( retval >= 0 )
result = relation == 0 ? true : false;
}
return retval;
}
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod, asIScriptContext *ctx)
{
return ExecuteString(engine, code, 0, asTYPEID_VOID, mod, ctx);
}
int ExecuteString(asIScriptEngine *engine, const char *code, void *ref, int refTypeId, asIScriptModule *mod, asIScriptContext *ctx)
{
// Wrap the code in a function so that it can be compiled and executed
string funcCode = " ExecuteString() {\n";
funcCode += code;
funcCode += "\n;}";
// Determine the return type based on the type of the ref arg
funcCode = engine->GetTypeDeclaration(refTypeId, true) + funcCode;
// GetModule will free unused types, so to be on the safe side we'll hold on to a reference to the type
asITypeInfo *type = 0;
if( refTypeId & asTYPEID_MASK_OBJECT )
{
type = engine->GetTypeInfoById(refTypeId);
if( type )
type->AddRef();
}
// If no module was provided, get a dummy from the engine
asIScriptModule *execMod = mod ? mod : engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE);
// Now it's ok to release the type
if( type )
type->Release();
// Compile the function that can be executed
asIScriptFunction *func = 0;
int r = execMod->CompileFunction("ExecuteString", funcCode.c_str(), -1, 0, &func);
if( r < 0 )
return r;
// If no context was provided, request a new one from the engine
asIScriptContext *execCtx = ctx ? ctx : engine->RequestContext();
r = execCtx->Prepare(func);
if( r < 0 )
{
func->Release();
if( !ctx ) execCtx->Release();
return r;
}
// Execute the function
r = execCtx->Execute();
// Unless the provided type was void retrieve it's value
if( ref != 0 && refTypeId != asTYPEID_VOID )
{
if( refTypeId & asTYPEID_OBJHANDLE )
{
// Expect the pointer to be null to start with
assert( *reinterpret_cast<void**>(ref) == 0 );
*reinterpret_cast<void**>(ref) = *reinterpret_cast<void**>(execCtx->GetAddressOfReturnValue());
engine->AddRefScriptObject(*reinterpret_cast<void**>(ref), engine->GetTypeInfoById(refTypeId));
}
else if( refTypeId & asTYPEID_MASK_OBJECT )
{
// Expect the pointer to point to a valid object
assert( *reinterpret_cast<void**>(ref) != 0 );
engine->AssignScriptObject(ref, execCtx->GetAddressOfReturnValue(), engine->GetTypeInfoById(refTypeId));
}
else
{
// Copy the primitive value
memcpy(ref, execCtx->GetAddressOfReturnValue(), engine->GetSizeOfPrimitiveType(refTypeId));
}
}
// Clean up
func->Release();
if( !ctx ) engine->ReturnContext(execCtx);
return r;
}
int WriteConfigToFile(asIScriptEngine *engine, const char *filename)
{
ofstream strm;
strm.open(filename);
return WriteConfigToStream(engine, strm);
}
int WriteConfigToStream(asIScriptEngine *engine, ostream &strm)
{
// A helper function for escaping quotes in default arguments
struct Escape
{
static string Quotes(const char *decl)
{
string str = decl;
size_t pos = 0;
for(;;)
{
// Find " characters
pos = str.find("\"",pos);
if( pos == string::npos )
break;
// Add a \ to escape them
str.insert(pos, "\\");
pos += 2;
}
return str;
}
};
int c, n;
asDWORD currAccessMask = 0;
string currNamespace = "";
engine->SetDefaultNamespace(currNamespace.c_str());
// Export the engine version, just for info
strm << "// AngelScript " << asGetLibraryVersion() << "\n";
strm << "// Lib options " << asGetLibraryOptions() << "\n";
// Export the relevant engine properties
strm << "// Engine properties\n";
for( n = 0; n < asEP_LAST_PROPERTY; n++ )
strm << "ep " << n << " " << engine->GetEngineProperty(asEEngineProp(n)) << "\n";
// Make sure the default array type is expanded to the template form
bool expandDefArrayToTempl = engine->GetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL) ? true : false;
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, true);
// Write enum types and their values
strm << "\n// Enums\n";
c = engine->GetEnumCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *ti = engine->GetEnumByIndex(n);
asDWORD accessMask = ti->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
const char *nameSpace = ti->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
const char *enumName = ti->GetName();
strm << "enum " << enumName << "\n";
for( asUINT m = 0; m < ti->GetEnumValueCount(); m++ )
{
const char *valName;
int val;
valName = ti->GetEnumValueByIndex(m, &val);
strm << "enumval " << enumName << " " << valName << " " << val << "\n";
}
}
// Enumerate all types
strm << "\n// Types\n";
// Keep a list of the template types, as the methods for these need to be exported first
set<asITypeInfo*> templateTypes;
c = engine->GetObjectTypeCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
asDWORD accessMask = type->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
const char *nameSpace = type->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
{
// This should only be interfaces
assert( type->GetSize() == 0 );
strm << "intf " << type->GetName() << "\n";
}
else
{
// Only the type flags are necessary. The application flags are application
// specific and doesn't matter to the offline compiler. The object size is also
// unnecessary for the offline compiler
strm << "objtype \"" << engine->GetTypeDeclaration(type->GetTypeId()) << "\" " << (unsigned int)(type->GetFlags() & asOBJ_MASK_VALID_FLAGS) << "\n";
// Store the template types (but not template instances)
if( (type->GetFlags() & asOBJ_TEMPLATE) && type->GetSubType() && (type->GetSubType()->GetFlags() & asOBJ_TEMPLATE_SUBTYPE) )
templateTypes.insert(type);
}
}
c = engine->GetTypedefCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *ti = engine->GetTypedefByIndex(n);
const char *nameSpace = ti->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
asDWORD accessMask = ti->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "typedef " << ti->GetName() << " \"" << engine->GetTypeDeclaration(ti->GetTypedefTypeId()) << "\"\n";
}
c = engine->GetFuncdefCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *funcDef = engine->GetFuncdefByIndex(n);
asDWORD accessMask = funcDef->GetAccessMask();
const char *nameSpace = funcDef->GetNamespace();
// Child funcdefs do not have any namespace, as they belong to the parent object
if( nameSpace && nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "funcdef \"" << funcDef->GetFuncdefSignature()->GetDeclaration() << "\"\n";
}
// A helper for writing object type members
struct TypeWriter
{
static void Write(asIScriptEngine *engine, ostream &strm, asITypeInfo *type, string &currNamespace, asDWORD &currAccessMask)
{
const char *nameSpace = type->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
string typeDecl = engine->GetTypeDeclaration(type->GetTypeId());
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
{
for( asUINT m = 0; m < type->GetMethodCount(); m++ )
{
asIScriptFunction *func = type->GetMethodByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "intfmthd " << typeDecl.c_str() << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
}
else
{
asUINT m;
for( m = 0; m < type->GetFactoryCount(); m++ )
{
asIScriptFunction *func = type->GetFactoryByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objbeh \"" << typeDecl.c_str() << "\" " << asBEHAVE_FACTORY << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
for( m = 0; m < type->GetBehaviourCount(); m++ )
{
asEBehaviours beh;
asIScriptFunction *func = type->GetBehaviourByIndex(m, &beh);
if( beh == asBEHAVE_CONSTRUCT )
// Prefix 'void'
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
else if( beh == asBEHAVE_DESTRUCT )
// Prefix 'void' and remove ~
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str()+1 << "\"\n";
else
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
for( m = 0; m < type->GetMethodCount(); m++ )
{
asIScriptFunction *func = type->GetMethodByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objmthd \"" << typeDecl.c_str() << "\" \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
for( m = 0; m < type->GetPropertyCount(); m++ )
{
asDWORD accessMask;
type->GetProperty(m, 0, 0, 0, 0, 0, 0, &accessMask);
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objprop \"" << typeDecl.c_str() << "\" \"" << type->GetPropertyDeclaration(m) << "\"\n";
}
}
}
};
// Write the members of the template types, so they can be fully registered before any other type uses them
// TODO: Order the template types based on dependency to avoid failure if one type uses instances of another
strm << "\n// Template type members\n";
for( set<asITypeInfo*>::iterator it = templateTypes.begin(); it != templateTypes.end(); ++it )
{
asITypeInfo *type = *it;
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
}
// Write the object types members
strm << "\n// Type members\n";
c = engine->GetObjectTypeCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
if( templateTypes.find(type) == templateTypes.end() )
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
}
// Write functions
strm << "\n// Functions\n";
c = engine->GetGlobalFunctionCount();
for( n = 0; n < c; n++ )
{
asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
const char *nameSpace = func->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "func \"" << Escape::Quotes(func->GetDeclaration()).c_str() << "\"\n";
}
// Write global properties
strm << "\n// Properties\n";
c = engine->GetGlobalPropertyCount();
for( n = 0; n < c; n++ )
{
const char *name;
int typeId;
bool isConst;
asDWORD accessMask;
const char *nameSpace;
engine->GetGlobalPropertyByIndex(n, &name, &nameSpace, &typeId, &isConst, 0, 0, &accessMask);
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
strm << "prop \"" << (isConst ? "const " : "") << engine->GetTypeDeclaration(typeId) << " " << name << "\"\n";
}
engine->SetDefaultNamespace("");
// Write string factory
strm << "\n// String factory\n";
asDWORD flags = 0;
int typeId = engine->GetStringFactoryReturnTypeId(&flags);
if( typeId > 0 )
strm << "strfactory \"" << ((flags & asTM_CONST) ? "const " : "") << engine->GetTypeDeclaration(typeId) << ((flags & asTM_INOUTREF) ? "&" : "") << "\"\n";
// Write default array type
strm << "\n// Default array type\n";
typeId = engine->GetDefaultArrayTypeId();
if( typeId > 0 )
strm << "defarray \"" << engine->GetTypeDeclaration(typeId) << "\"\n";
// Restore original settings
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, expandDefArrayToTempl);
return 0;
}
int ConfigEngineFromStream(asIScriptEngine *engine, istream &strm, const char *configFile)
{
int r;
// Some helper functions for parsing the configuration
struct in
{
static asETokenClass GetToken(asIScriptEngine *engine, string &token, const string &text, asUINT &pos)
{
asUINT len = 0;
asETokenClass t = engine->ParseToken(&text[pos], text.length() - pos, &len);
while( (t == asTC_WHITESPACE || t == asTC_COMMENT) && pos < text.length() )
{
pos += len;
t = engine->ParseToken(&text[pos], text.length() - pos, &len);
}
token.assign(&text[pos], len);
pos += len;
return t;
}
static void ReplaceSlashQuote(string &str)
{
size_t pos = 0;
for(;;)
{
// Search for \" in the string
pos = str.find("\\\"", pos);
if( pos == string::npos )
break;
// Remove the \ character
str.erase(pos, 1);
}
}
static asUINT GetLineNumber(const string &text, asUINT pos)
{
asUINT count = 1;
for( asUINT n = 0; n < pos; n++ )
if( text[n] == '\n' )
count++;
return count;
}
};
// Since we are only going to compile the script and never actually execute it,
// we turn off the initialization of global variables, so that the compiler can
// just register dummy types and functions for the application interface.
r = engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false); assert( r >= 0 );
// Read the entire file
char buffer[1000];
string config;
do {
strm.getline(buffer, 1000);
config += buffer;
config += "\n";
} while( !strm.eof() );
// Process the configuration file and register each entity
asUINT pos = 0;
while( pos < config.length() )
{
string token;
// TODO: The position where the initial token is found should be stored for error messages
in::GetToken(engine, token, config, pos);
if( token == "ep" )
{
string tmp;
in::GetToken(engine, tmp, config, pos);
asEEngineProp ep = asEEngineProp(atol(tmp.c_str()));
// Only set properties that affect the compiler
if( ep != asEP_COPY_SCRIPT_SECTIONS &&
ep != asEP_MAX_STACK_SIZE &&
ep != asEP_INIT_GLOBAL_VARS_AFTER_BUILD &&
ep != asEP_EXPAND_DEF_ARRAY_TO_TMPL &&
ep != asEP_AUTO_GARBAGE_COLLECT )
{
// Get the value for the property
in::GetToken(engine, tmp, config, pos);
stringstream s(tmp);
asPWORD value;
s >> value;
engine->SetEngineProperty(ep, value);
}
}
else if( token == "namespace" )
{
string ns;
in::GetToken(engine, ns, config, pos);
ns = ns.substr(1, ns.length() - 2);
r = engine->SetDefaultNamespace(ns.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to set namespace");
return -1;
}
}
else if( token == "access" )
{
string maskStr;
in::GetToken(engine, maskStr, config, pos);
asDWORD mask = strtoul(maskStr.c_str(), 0, 16);
engine->SetDefaultAccessMask(mask);
}
else if( token == "objtype" )
{
string name, flags;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, flags, config, pos);
// The size of the value type doesn't matter, because the
// engine must adjust it anyway for different platforms
r = engine->RegisterObjectType(name.c_str(), (atol(flags.c_str()) & asOBJ_VALUE) ? 1 : 0, atol(flags.c_str()));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object type");
return -1;
}
}
else if( token == "objbeh" )
{
string name, behaviour, decl;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, behaviour, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
// Remove the $ that the engine prefixes the behaviours with
size_t n = decl.find("$");
if( n != string::npos )
decl[n] = ' ';
asEBehaviours behave = static_cast<asEBehaviours>(atol(behaviour.c_str()));
if( behave == asBEHAVE_TEMPLATE_CALLBACK )
{
// TODO: How can we let the compiler register this? Maybe through a plug-in system? Or maybe by implementing the callback as a script itself
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register template callback without the actual implementation");
}
else
{
r = engine->RegisterObjectBehaviour(name.c_str(), behave, decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register behaviour");
return -1;
}
}
}
else if( token == "objmthd" )
{
string name, decl;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterObjectMethod(name.c_str(), decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object method");
return -1;
}
}
else if( token == "objprop" )
{
string name, decl;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
asITypeInfo *type = engine->GetTypeInfoById(engine->GetTypeIdByDecl(name.c_str()));
if( type == 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Type doesn't exist for property registration");
return -1;
}
// All properties must have different offsets in order to make them
// distinct, so we simply register them with an incremental offset
r = engine->RegisterObjectProperty(name.c_str(), decl.c_str(), type->GetPropertyCount());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object property");
return -1;
}
}
else if( token == "intf" )
{
string name, size, flags;
in::GetToken(engine, name, config, pos);
r = engine->RegisterInterface(name.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface");
return -1;
}
}
else if( token == "intfmthd" )
{
string name, decl;
in::GetToken(engine, name, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterInterfaceMethod(name.c_str(), decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface method");
return -1;
}
}
else if( token == "func" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterGlobalFunction(decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global function");
return -1;
}
}
else if( token == "prop" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
// All properties must have different offsets in order to make them
// distinct, so we simply register them with an incremental offset.
// The pointer must also be non-null so we add 1 to have a value.
r = engine->RegisterGlobalProperty(decl.c_str(), reinterpret_cast<void*>(asPWORD(engine->GetGlobalPropertyCount()+1)));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global property");
return -1;
}
}
else if( token == "strfactory" )
{
string type;
in::GetToken(engine, type, config, pos);
type = type.substr(1, type.length() - 2);
r = engine->RegisterStringFactory(type.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register string factory");
return -1;
}
}
else if( token == "defarray" )
{
string type;
in::GetToken(engine, type, config, pos);
type = type.substr(1, type.length() - 2);
r = engine->RegisterDefaultArrayType(type.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register the default array type");
return -1;
}
}
else if( token == "enum" )
{
string type;
in::GetToken(engine, type, config, pos);
r = engine->RegisterEnum(type.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum type");
return -1;
}
}
else if( token == "enumval" )
{
string type, name, value;
in::GetToken(engine, type, config, pos);
in::GetToken(engine, name, config, pos);
in::GetToken(engine, value, config, pos);
r = engine->RegisterEnumValue(type.c_str(), name.c_str(), atol(value.c_str()));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum value");
return -1;
}
}
else if( token == "typedef" )
{
string type, decl;
in::GetToken(engine, type, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
r = engine->RegisterTypedef(type.c_str(), decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register typedef");
return -1;
}
}
else if( token == "funcdef" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
r = engine->RegisterFuncdef(decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register funcdef");
return -1;
}
}
}
return 0;
}
string GetExceptionInfo(asIScriptContext *ctx, bool showStack)
{
if( ctx->GetState() != asEXECUTION_EXCEPTION ) return "";
stringstream text;
const asIScriptFunction *function = ctx->GetExceptionFunction();
text << "func: " << function->GetDeclaration() << "\n";
text << "modl: " << function->GetModuleName() << "\n";
text << "sect: " << function->GetScriptSectionName() << "\n";
text << "line: " << ctx->GetExceptionLineNumber() << "\n";
text << "desc: " << ctx->GetExceptionString() << "\n";
if( showStack )
{
text << "--- call stack ---\n";
for( asUINT n = 1; n < ctx->GetCallstackSize(); n++ )
{
function = ctx->GetFunction(n);
if( function )
{
if( function->GetFuncType() == asFUNC_SCRIPT )
{
text << function->GetScriptSectionName() << " (" << ctx->GetLineNumber(n) << "): " << function->GetDeclaration() << "\n";
}
else
{
// The context is being reused by the application for a nested call
text << "{...application...}: " << function->GetDeclaration() << "\n";
}
}
else
{
// The context is being reused by the script engine for a nested call
text << "{...script engine...}\n";
}
}
}
return text.str();
}
END_AS_NAMESPACE
+743
View File
@@ -0,0 +1,743 @@
#include <assert.h>
#include <string.h>
#include "scriptmap.h"
#include "scriptarray.h"
#include "../source/as_scriptengine.h"
#include "../source/as_scriptobject.h"
BEGIN_AS_NAMESPACE
using namespace std;
extern bool IsHandleCompatibleWithObject(asCScriptEngine* engine, void *obj, int objTypeId, int handleTypeId);
//--------------------------------------------------------------------------
// CScriptMap implementation
CScriptMap::CScriptMap(asIScriptEngine *engine)
{
// We start with one reference
refCount.set(1);
gcFlag = false;
// Start at the zeroth modification
modCount = 0;
// Keep a reference to the engine for as long as we live
// We don't increment the reference counter, because the
// engine will hold a pointer to the object.
this->engine = engine;
// Notify the garbage collector of this object
// TODO: The type id should be cached
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("map"));
}
CScriptMap::~CScriptMap()
{
// Delete all keys and values
DeleteAll();
}
void CScriptMap::AddRef() const
{
// We need to clear the GC flag
gcFlag = false;
refCount.atomicInc();
}
void CScriptMap::Release() const
{
// We need to clear the GC flag
gcFlag = false;
if( refCount.atomicDec() == 0 )
delete this;
}
int CScriptMap::GetRefCount()
{
return refCount.get();
}
void CScriptMap::SetGCFlag()
{
gcFlag = true;
}
bool CScriptMap::GetGCFlag()
{
return gcFlag;
}
void CScriptMap::EnumReferences(asIScriptEngine *engine)
{
// Call the gc enum callback for each of the objects
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_MASK_OBJECT )
engine->GCEnumCallback(it->second.valueObj);
}
}
void CScriptMap::ReleaseAllReferences(asIScriptEngine * /*engine*/)
{
// We're being told to release all references in
// order to break circular references for dead objects
DeleteAll();
}
CScriptMap &CScriptMap::operator =(const CScriptMap &other)
{
// Clear everything we had before
DeleteAll();
// Do a shallow copy of the map
mapType::const_iterator it;
for( it = other.dict.begin(); it != other.dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_OBJHANDLE )
Set(it->first, (void*)&it->second.valueObj, it->second.typeId);
else if( it->second.typeId & asTYPEID_MASK_OBJECT )
Set(it->first, (void*)it->second.valueObj, it->second.typeId);
else
Set(it->first, (void*)&it->second.valueInt, it->second.typeId);
}
return *this;
}
void CScriptMap::Set(asINT64 key, void *value, int typeId)
{
valueStruct valStruct = {{0},0};
valStruct.typeId = typeId;
if( typeId & asTYPEID_OBJHANDLE )
{
// We're receiving a reference to the handle, so we need to dereference it
valStruct.valueObj = *(void**)value;
engine->AddRefScriptObject(valStruct.valueObj, engine->GetTypeInfoById(typeId));
}
else if( typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
valStruct.valueObj = engine->CreateScriptObjectCopy(value, engine->GetTypeInfoById(typeId));
}
else
{
// Copy the primitive value
// We receive a pointer to the value.
int size = engine->GetSizeOfPrimitiveType(typeId);
memcpy(&valStruct.valueInt, value, size);
}
mapType::iterator it;
it = dict.find(key);
if( it != dict.end() )
{
FreeValue(it->second);
// Insert the new value
it->second = valStruct;
}
else
{
dict.insert(mapType::value_type(key, valStruct));
++modCount;
}
}
// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the map as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a
// different type.
void CScriptMap::Set(asINT64 key, asINT64 &value)
{
Set(key, &value, asTYPEID_INT64);
}
// This overloaded method is implemented so that all floating point types
// will be stored in the map as double through implicit conversions.
// This simplifies the management of the numeric types when the script
// retrieves the stored value using a different type.
void CScriptMap::Set(asINT64 key, double &value)
{
Set(key, &value, asTYPEID_DOUBLE);
}
// This helper function exists to assist various map and iterator
// methods that want to retrieve a value from a standard iterator
bool CScriptMap::Iterator_GetValue(CScriptMap::mapType::const_iterator &it, void *value, int typeId) const {
// Return the value
if( typeId & asTYPEID_OBJHANDLE )
{
// A handle can be retrieved if the stored type is a handle of same or compatible type
// or if the stored type is an object that implements the interface that the handle refer to.
if( (it->second.typeId & asTYPEID_MASK_OBJECT) &&
IsHandleCompatibleWithObject((asCScriptEngine*)engine, it->second.valueObj, it->second.typeId, typeId) )
{
engine->AddRefScriptObject(it->second.valueObj, engine->GetTypeInfoById(it->second.typeId));
*(void**)value = it->second.valueObj;
return true;
}
}
else if( typeId & asTYPEID_MASK_OBJECT )
{
// Verify that the copy can be made
bool isCompatible = false;
if( it->second.typeId == typeId )
isCompatible = true;
// Copy the object into the given reference
if( isCompatible )
{
engine->AssignScriptObject(value, it->second.valueObj, engine->GetTypeInfoById(typeId));
return true;
}
}
else
{
if( it->second.typeId == typeId )
{
int size = engine->GetSizeOfPrimitiveType(typeId);
memcpy(value, &it->second.valueInt, size);
return true;
}
// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
if( it->second.typeId == asTYPEID_INT64 && typeId == asTYPEID_DOUBLE )
{
*(double*)value = double(it->second.valueInt);
return true;
}
else if( it->second.typeId == asTYPEID_DOUBLE && typeId == asTYPEID_INT64 )
{
*(asINT64*)value = asINT64(it->second.valueFlt);
return true;
}
}
// AngelScript has already initialized the value with a default value,
// so we don't have to do anything if we don't find the element, or if
// the element is incompatible with the requested type.
return false;
}
// Returns true if the value was successfully retrieved
bool CScriptMap::Get(asINT64 key, void *value, int typeId) const
{
mapType::const_iterator it;
it = dict.find(key);
if( it != dict.end() )
{
// This logic is already implemented in the iterator wrapper,
// so defer to that function from here
return Iterator_GetValue(it, value, typeId);
}
return false;
}
bool CScriptMap::Get(asINT64 key, asINT64 &value) const
{
return Get(key, &value, asTYPEID_INT64);
}
bool CScriptMap::Get(asINT64 key, double &value) const
{
return Get(key, &value, asTYPEID_DOUBLE);
}
bool CScriptMap::Exists(asINT64 key) const
{
mapType::const_iterator it;
it = dict.find(key);
if( it != dict.end() )
return true;
return false;
}
bool CScriptMap::IsEmpty() const
{
if( dict.size() == 0 )
return true;
return false;
}
asUINT CScriptMap::GetSize() const
{
return asUINT(dict.size());
}
void CScriptMap::Delete(asINT64 key)
{
mapType::iterator it;
it = dict.find(key);
if( it != dict.end() )
{
FreeValue(it->second);
dict.erase(it);
++modCount;
}
}
void CScriptMap::DeleteAll()
{
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
FreeValue(it->second);
dict.clear();
}
void CScriptMap::DeleteNulls()
{
mapType::iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
if( it->second.typeId & asTYPEID_OBJHANDLE )
if( it->second.valueObj == 0 )
it = dict.erase(it);
}
++modCount;
}
void CScriptMap::FreeValue(valueStruct &value)
{
// If it is a handle or a ref counted object, call release
if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Let the engine release the object
engine->ReleaseScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
value.valueObj = 0;
value.typeId = 0;
}
// For primitives, there's nothing to do
}
CScriptArray* CScriptMap::GetKeys() const
{
// TODO: optimize: The string array type should only be determined once.
// It should be recomputed when registering the map class.
// Only problem is if multiple engines are used, as they may not
// share the same type id. Alternatively it can be stored in the
// user data for the map type.
int stringArrayType = engine->GetTypeIdByDecl("array<int64>");
asITypeInfo *ot = engine->GetTypeInfoById(stringArrayType);
// Create the array object
CScriptArray *array = new CScriptArray(dict.size(), ot);
long current = -1;
mapType::const_iterator it;
for( it = dict.begin(); it != dict.end(); it++ )
{
current++;
*(asINT64*)array->At(current) = it->first;
}
return array;
}
CScriptMap::Iterator CScriptMap::GetIterator() const {
return Iterator(this);
}
void CScriptMap::Delete(CScriptMap::Iterator& it) {
if( it.container != this ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Mismatched script container and iterator.");
return;
}
if( it.it == dict.end() ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Deleting invalid iterator.");
return;
}
it.it = dict.erase(it.it);
it.first = true;
++modCount;
it.modCount = modCount;
}
//--------------------------------------------------------------------------
// Iterator implementation
CScriptMap::Iterator::Iterator() {
container = 0;
}
CScriptMap::Iterator::Iterator(const CScriptMap *container) {
this->container = container;
// Start the iteration
it = container->dict.begin();
first = true;
// Track the modification count of the map
// so we can safely fail when the map is altered
modCount = container->modCount;
// Keep a reference to the map so it doesn't
// get destroyed from under us
container->AddRef();
}
CScriptMap::Iterator::~Iterator() {
// Release map reference
if(container)
container->Release();
}
static void DefaultConstructIterator(void *memory) {
new(memory) CScriptMap::Iterator();
}
static void ConstructIterator(void *memory, const CScriptMap *container) {
new(memory) CScriptMap::Iterator(container);
}
static void DestructIterator(CScriptMap::Iterator &it) {
it.~Iterator();
}
asINT64 CScriptMap::Iterator::GetKey() {
if( !container || modCount != container->modCount || it == container->dict.end() ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Iterator not valid or map changed.");
return 0;
}
return it->first;
}
bool CScriptMap::Iterator::Iterate(void *value, int typeId) {
return Iterate(0, value, typeId);
}
bool CScriptMap::Iterator::Iterate(asINT64* key, void *value, int typeId) {
if( !container ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Iterating on invalid iterator.");
return false;
}
if( modCount != container->modCount ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Dictionary changed during iteration.");
return false;
}
if( it == container->dict.end() )
return false;
if( first )
first = false;
else
++it;
if( it == container->dict.end() )
return false;
if( key )
*key = it->first;
if( container->Iterator_GetValue(it, value, typeId) )
{
return true;
}
else
{
// The caller can check IsValid to see if the iteration can still
// continue because the type was wrong, or whether it has really ended
return false;
}
}
bool CScriptMap::Iterator::IsValid() {
if( !container || modCount != container->modCount ) {
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Map changed during iteration.");
}
return it != container->dict.end();
}
CScriptMap::Iterator &CScriptMap::Iterator::operator =(const CScriptMap::Iterator &other) {
if(other.container)
other.container->AddRef();
if(container)
container->Release();
modCount = other.modCount;
container = other.container;
first = other.first;
it = other.it;
return *this;
}
//--------------------------------------------------------------------------
// Generic wrappers
void ScriptMapFactory_Generic(asIScriptGeneric *gen)
{
*(CScriptMap**)gen->GetAddressOfReturnLocation() = new CScriptMap(gen->GetEngine());
}
void ScriptMapAddRef_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
dict->AddRef();
}
void ScriptMapRelease_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
dict->Release();
}
void ScriptMapAssign_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
CScriptMap *other = *(CScriptMap**)gen->GetAddressOfArg(0);
*dict = *other;
*(CScriptMap**)gen->GetAddressOfReturnLocation() = dict;
}
void ScriptMapSet_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
int typeId = gen->GetArgTypeId(1);
dict->Set(key, ref, typeId);
}
void ScriptMapSetInt_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
dict->Set(key, *(asINT64*)ref);
}
void ScriptMapSetFlt_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
dict->Set(key, *(double*)ref);
}
void ScriptMapGet_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
int typeId = gen->GetArgTypeId(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(key, ref, typeId);
}
void ScriptMapGetInt_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(key, *(asINT64*)ref);
}
void ScriptMapGetFlt_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
void *ref = *(void**)gen->GetAddressOfArg(1);
*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(key, *(double*)ref);
}
void ScriptMapExists_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
bool ret = dict->Exists(key);
*(bool*)gen->GetAddressOfReturnLocation() = ret;
}
void ScriptMapDelete_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
asINT64 key = *(asINT64*)gen->GetAddressOfArg(0);
dict->Delete(key);
}
void ScriptMapDeleteAll_Generic(asIScriptGeneric *gen)
{
CScriptMap *dict = (CScriptMap*)gen->GetObject();
dict->DeleteAll();
}
static void ScriptMapGetRefCount_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}
static void ScriptMapSetGCFlag_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
self->SetGCFlag();
}
static void ScriptMapGetGCFlag_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag();
}
static void ScriptMapEnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->EnumReferences(engine);
}
static void ScriptMapReleaseAllReferences_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->ReleaseAllReferences(engine);
}
static void CScriptMapGetKeys_Generic(asIScriptGeneric *gen)
{
CScriptMap *self = (CScriptMap*)gen->GetObject();
*(CScriptArray**)gen->GetAddressOfReturnLocation() = self->GetKeys();
}
//--------------------------------------------------------------------------
// Register the type
void RegisterScriptMap(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptMap_Generic(engine);
else
RegisterScriptMap_Native(engine);
}
void RegisterScriptMap_Native(asIScriptEngine *engine)
{
int r;
//Register iterator type so we can use it later
r = engine->RegisterObjectType("map_iterator", sizeof(CScriptMap::Iterator), asOBJ_VALUE | asOBJ_APP_CLASS_CDA); assert( r >= 0 );
r = engine->RegisterObjectType("map", sizeof(CScriptMap), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
// Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context
r = engine->RegisterObjectBehaviour("map", asBEHAVE_FACTORY, "map@ f()", asFUNCTION(ScriptMapFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptMap,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptMap,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "map &opAssign(const map &in)", asMETHODPR(CScriptMap, operator=, (const CScriptMap &), CScriptMap&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, const ?&in)", asMETHODPR(CScriptMap,Set,(asINT64,void*,int),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, ?&out) const", asMETHODPR(CScriptMap,Get,(asINT64,void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, int64&in)", asMETHODPR(CScriptMap,Set,(asINT64,asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, int64&out) const", asMETHODPR(CScriptMap,Get,(asINT64,asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, double&in)", asMETHODPR(CScriptMap,Set,(asINT64,double&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, double&out) const", asMETHODPR(CScriptMap,Get,(asINT64,double&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool exists(int64) const", asMETHOD(CScriptMap,Exists), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool isEmpty() const", asMETHOD(CScriptMap, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "uint getSize() const", asMETHOD(CScriptMap, GetSize), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void delete(int64)", asMETHODPR(CScriptMap, Delete, (asINT64), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void delete(map_iterator&)", asMETHODPR(CScriptMap, Delete, (CScriptMap::Iterator&), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void deleteAll()", asMETHOD(CScriptMap,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void deleteNulls()", asMETHOD(CScriptMap,DeleteNulls), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "map_iterator iterator()", asMETHOD(CScriptMap,GetIterator), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "array<int64> @getKeys() const", asMETHOD(CScriptMap,GetKeys), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("map", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptMap,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptMap,SetGCFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptMap,GetGCFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptMap,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptMap,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 );
#if AS_USE_STLNAMES == 1
// Same as isEmpty
r = engine->RegisterObjectMethod("map", "bool empty() const", asMETHOD(CScriptMap, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
// Same as getSize
r = engine->RegisterObjectMethod("map", "uint size() const", asMETHOD(CScriptMap, GetSize), asCALL_THISCALL); assert( r >= 0 );
// Same as delete
r = engine->RegisterObjectMethod("map", "void erase(asINT64 in)", asMETHOD(CScriptMap,Delete), asCALL_THISCALL); assert( r >= 0 );
// Same as deleteAll
r = engine->RegisterObjectMethod("map", "void clear()", asMETHOD(CScriptMap,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
#endif
// Register iterator methods
r = engine->RegisterObjectBehaviour("map_iterator", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(DefaultConstructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("map_iterator", asBEHAVE_CONSTRUCT, "void f(const map &in)", asFUNCTION(ConstructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("map_iterator", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructIterator), asCALL_CDECL_OBJFIRST); assert( r>= 0 );
r = engine->RegisterObjectMethod("map_iterator", "int64 get_key()", asMETHOD(CScriptMap::Iterator, GetKey), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("map_iterator", "bool iterate(?&out)", asMETHODPR(CScriptMap::Iterator, Iterate, (void*,int), bool), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("map_iterator", "bool iterate(int64 &out, ?&out)", asMETHODPR(CScriptMap::Iterator, Iterate, (asINT64*,void*,int), bool), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("map_iterator", "bool get_valid()", asMETHOD(CScriptMap::Iterator, IsValid), asCALL_THISCALL); assert( r>= 0 );
r = engine->RegisterObjectMethod("map_iterator", "map_iterator& opAssign(const map_iterator &in)", asMETHOD(CScriptMap::Iterator, operator=), asCALL_THISCALL); assert( r>= 0 );
}
void RegisterScriptMap_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("map", sizeof(CScriptMap), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_FACTORY, "map@ f()", asFUNCTION(ScriptMapFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptMapAddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptMapRelease_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "map &opAssign(const map &in)", asFUNCTION(ScriptMapAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, ?&in)", asFUNCTION(ScriptMapSet_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, ?&out) const", asFUNCTION(ScriptMapGet_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, int64&in)", asFUNCTION(ScriptMapSetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, int64&out) const", asFUNCTION(ScriptMapGetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void set(int64, double&in)", asFUNCTION(ScriptMapSetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool get(int64, double&out) const", asFUNCTION(ScriptMapGetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "bool exists(asINT64 in) const", asFUNCTION(ScriptMapExists_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void delete(asINT64 in)", asFUNCTION(ScriptMapDelete_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "void deleteAll()", asFUNCTION(ScriptMapDeleteAll_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("map", "array<int64> @getKeys() const", asFUNCTION(CScriptMapGetKeys_Generic), asCALL_GENERIC); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("map", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptMapGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptMapSetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptMapGetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptMapEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("map", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptMapReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
}
END_AS_NAMESPACE
+357
View File
@@ -0,0 +1,357 @@
#include <assert.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include "scriptmath.h"
#ifdef __BORLANDC__
#include <cmath>
// The C++Builder RTL doesn't pull the *f functions into the global namespace per default.
using namespace std;
#if __BORLANDC__ < 0x580
// C++Builder 6 and earlier don't come with any *f variants of the math functions at all.
inline float cosf (float arg) { return std::cos (arg); }
inline float sinf (float arg) { return std::sin (arg); }
inline float tanf (float arg) { return std::tan (arg); }
inline float atan2f (float y, float x) { return std::atan2 (y, x); }
inline float logf (float arg) { return std::log (arg); }
inline float powf (float x, float y) { return std::pow (x, y); }
inline float sqrtf (float arg) { return std::sqrt (arg); }
#endif
// C++Builder doesn't define most of the non-standard float-specific math functions with
// "*f" suffix; instead it provides overloads for the standard math functions which take
// "float" arguments.
inline float acosf (float arg) { return std::acos (arg); }
inline float asinf (float arg) { return std::asin (arg); }
inline float atanf (float arg) { return std::atan (arg); }
inline float coshf (float arg) { return std::cosh (arg); }
inline float sinhf (float arg) { return std::sinh (arg); }
inline float tanhf (float arg) { return std::tanh (arg); }
inline float log10f (float arg) { return std::log10 (arg); }
inline float ceilf (float arg) { return std::ceil (arg); }
inline float fabsf (float arg) { return std::fabs (arg); }
inline float floorf (float arg) { return std::floor (arg); }
// C++Builder doesn't define a non-standard "modff" function but rather an overload of "modf"
// for float arguments. However, BCC's float overload of fmod() is broken (QC #74816; fixed
// in C++Builder 2010).
inline float modff (float x, float *y)
{
double d;
float f = (float) modf((double) x, &d);
*y = (float) d;
return f;
}
#endif
BEGIN_AS_NAMESPACE
// Determine whether the float version should be registered, or the double version
#ifndef AS_USE_FLOAT
#if !defined(_WIN32_WCE) // WinCE doesn't have the float versions of the math functions
#define AS_USE_FLOAT 1
#endif
#endif
// The modf function doesn't seem very intuitive, so I'm writing this
// function that simply returns the fractional part of the float value
#if AS_USE_FLOAT
float fractionf(float v)
{
float intPart;
return modff(v, &intPart);
}
#else
double fraction(double v)
{
double intPart;
return modf(v, &intPart);
}
#endif
// As AngelScript doesn't allow bitwise manipulation of float types we'll provide a couple of
// functions for converting float values to IEEE 754 formatted values etc. This also allow us to
// provide a platform agnostic representation to the script so the scripts don't have to worry
// about whether the CPU uses IEEE 754 floats or some other representation
float fpFromIEEE(asUINT raw)
{
// TODO: Identify CPU family to provide proper conversion
// if the CPU doesn't natively use IEEE style floats
return *reinterpret_cast<float*>(&raw);
}
asUINT fpToIEEE(float fp)
{
return *reinterpret_cast<asUINT*>(&fp);
}
double fpFromIEEE(asQWORD raw)
{
return *reinterpret_cast<double*>(&raw);
}
asQWORD fpToIEEE(double fp)
{
return *reinterpret_cast<asQWORD*>(&fp);
}
float _round(float v) {
return floorf(v + 0.5f);
}
double _round(double v) {
return floorl(v + 0.5);
}
// closeTo() is used to determine if the binary representation of two numbers are
// relatively close to each other. Numerical errors due to rounding errors build
// up over many operations, so it is almost impossible to get exact numbers and
// this is where closeTo() comes in.
//
// It shouldn't be used to determine if two numbers are mathematically close to
// each other.
//
// ref: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
// ref: http://www.gamedev.net/topic/653449-scriptmath-and-closeto/
bool closeTo(float a, float b, float epsilon)
{
// Equal numbers and infinity will return immediately
if( a == b ) return true;
// When very close to 0, we can use the absolute comparison
float diff = fabsf(a - b);
if( (a == 0 || b == 0) && (diff < epsilon) )
return true;
// Otherwise we need to use relative comparison to account for precision
return diff / (fabs(a) + fabs(b)) < epsilon;
}
bool closeTo(double a, double b, double epsilon)
{
if( a == b ) return true;
double diff = fabs(a - b);
if( (a == 0 || b == 0) && (diff < epsilon) )
return true;
return diff / (fabs(a) + fabs(b)) < epsilon;
}
void RegisterScriptMath_Native(asIScriptEngine *engine)
{
int r;
// Conversion between floating point and IEEE bits representations
r = engine->RegisterGlobalFunction("float fpFromIEEE(uint)", asFUNCTIONPR(fpFromIEEE, (asUINT), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint fpToIEEE(float)", asFUNCTIONPR(fpToIEEE, (float), asUINT), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fpFromIEEE(uint64)", asFUNCTIONPR(fpFromIEEE, (asQWORD), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint64 fpToIEEE(double)", asFUNCTIONPR(fpToIEEE, (double), asQWORD), asCALL_CDECL); assert( r >= 0 );
// Close to comparison with epsilon
r = engine->RegisterGlobalFunction("bool closeTo(float, float, float = 0.00001f)", asFUNCTIONPR(closeTo, (float, float, float), bool), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("bool closeTo(double, double, double = 0.0000000001)", asFUNCTIONPR(closeTo, (double, double, double), bool), asCALL_CDECL); assert( r >= 0 );
#if AS_USE_FLOAT
// Trigonometric functions
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTIONPR(cosf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTIONPR(sinf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTIONPR(tanf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTIONPR(acosf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTIONPR(asinf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTIONPR(atanf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTIONPR(atan2f, (float, float), float), asCALL_CDECL); assert( r >= 0 );
// Hyberbolic functions
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTIONPR(coshf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTIONPR(sinhf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTIONPR(tanhf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Exponential and logarithmic functions
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTIONPR(logf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTIONPR(log10f, (float), float), asCALL_CDECL); assert( r >= 0 );
// Power functions
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTIONPR(powf, (float, float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTIONPR(sqrtf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Nearest integer, absolute value, and remainder functions
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTIONPR(ceilf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTIONPR(fabsf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTIONPR(floorf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float round(float)", asFUNCTIONPR(_round, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTIONPR(fractionf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Don't register modf because AngelScript already supports the % operator
#else
// double versions of the same
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTIONPR(cos, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTIONPR(sin, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTIONPR(tan, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTIONPR(acos, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTIONPR(asin, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTIONPR(atan, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTIONPR(atan2, (double, double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTIONPR(cosh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTIONPR(sinh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTIONPR(tanh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTIONPR(log, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTIONPR(log10, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTIONPR(pow, (double, double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTIONPR(sqrt, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTIONPR(ceil, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTIONPR(fabs, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTIONPR(floor, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double round(double)", asFUNCTIONPR(_round, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTIONPR(fraction, (double), double), asCALL_CDECL); assert( r >= 0 );
#endif
}
#if AS_USE_FLOAT
// This macro creates simple generic wrappers for functions of type 'float func(float)'
#define GENERICff(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
float f = *(float*)gen->GetAddressOfArg(0); \
*(float*)gen->GetAddressOfReturnLocation() = x(f); \
}
GENERICff(cosf)
GENERICff(sinf)
GENERICff(tanf)
GENERICff(acosf)
GENERICff(asinf)
GENERICff(atanf)
GENERICff(coshf)
GENERICff(sinhf)
GENERICff(tanhf)
GENERICff(logf)
GENERICff(log10f)
GENERICff(sqrtf)
GENERICff(ceilf)
GENERICff(fabsf)
GENERICff(floorf)
GENERICff(fractionf)
void powf_generic(asIScriptGeneric *gen)
{
float f1 = *(float*)gen->GetAddressOfArg(0);
float f2 = *(float*)gen->GetAddressOfArg(1);
*(float*)gen->GetAddressOfReturnLocation() = powf(f1, f2);
}
void atan2f_generic(asIScriptGeneric *gen)
{
float f1 = *(float*)gen->GetAddressOfArg(0);
float f2 = *(float*)gen->GetAddressOfArg(1);
*(float*)gen->GetAddressOfReturnLocation() = atan2f(f1, f2);
}
#else
// This macro creates simple generic wrappers for functions of type 'double func(double)'
#define GENERICdd(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
double f = *(double*)gen->GetAddressOfArg(0); \
*(double*)gen->GetAddressOfReturnLocation() = x(f); \
}
GENERICdd(cos)
GENERICdd(sin)
GENERICdd(tan)
GENERICdd(acos)
GENERICdd(asin)
GENERICdd(atan)
GENERICdd(cosh)
GENERICdd(sinh)
GENERICdd(tanh)
GENERICdd(log)
GENERICdd(log10)
GENERICdd(sqrt)
GENERICdd(ceil)
GENERICdd(fabs)
GENERICdd(floor)
GENERICdd(fraction)
void pow_generic(asIScriptGeneric *gen)
{
double f1 = *(double*)gen->GetAddressOfArg(0);
double f2 = *(double*)gen->GetAddressOfArg(1);
*(double*)gen->GetAddressOfReturnLocation() = pow(f1, f2);
}
void atan2_generic(asIScriptGeneric *gen)
{
double f1 = *(double*)gen->GetAddressOfArg(0);
double f2 = *(double*)gen->GetAddressOfArg(1);
*(double*)gen->GetAddressOfReturnLocation() = atan2(f1, f2);
}
#endif
void RegisterScriptMath_Generic(asIScriptEngine *engine)
{
int r;
#if AS_USE_FLOAT
// Trigonometric functions
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTION(cosf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTION(sinf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTION(tanf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTION(acosf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTION(asinf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTION(atanf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTION(atan2f_generic), asCALL_GENERIC); assert( r >= 0 );
// Hyberbolic functions
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTION(coshf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTION(sinhf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTION(tanhf_generic), asCALL_GENERIC); assert( r >= 0 );
// Exponential and logarithmic functions
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTION(logf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTION(log10f_generic), asCALL_GENERIC); assert( r >= 0 );
// Power functions
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTION(powf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTION(sqrtf_generic), asCALL_GENERIC); assert( r >= 0 );
// Nearest integer, absolute value, and remainder functions
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTION(ceilf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTION(fabsf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTION(floorf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTION(fractionf_generic), asCALL_GENERIC); assert( r >= 0 );
// Don't register modf because AngelScript already supports the % operator
#else
// double versions of the same
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTION(cos_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTION(sin_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTION(tan_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTION(acos_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTION(asin_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTION(atan_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTION(atan2_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTION(cosh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTION(sinh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTION(tanh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTION(log_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTION(log10_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTION(pow_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTION(sqrt_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTION(ceil_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTION(fabs_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTION(floor_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTION(fraction_generic), asCALL_GENERIC); assert( r >= 0 );
#endif
}
void RegisterScriptMath(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptMath_Generic(engine);
else
RegisterScriptMath_Native(engine);
}
END_AS_NAMESPACE
+639
View File
@@ -0,0 +1,639 @@
#include <assert.h>
#include <sstream>
#include "scriptstdstring.h"
#include <string.h> // strstr
using namespace std;
BEGIN_AS_NAMESPACE
static void StringFactoryGeneric(asIScriptGeneric *gen) {
asUINT length = gen->GetArgDWord(0);
const char *s = (const char*)gen->GetArgAddress(1);
string str(s, length);
gen->SetReturnObject(&str);
}
static void ConstructStringGeneric(asIScriptGeneric * gen) {
new (gen->GetObject()) string();
}
static void CopyConstructStringGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetArgObject(0));
new (gen->GetObject()) string(*a);
}
static void DestructStringGeneric(asIScriptGeneric * gen) {
string * ptr = static_cast<string *>(gen->GetObject());
ptr->~string();
}
static void AssignStringGeneric(asIScriptGeneric *gen) {
string * a = static_cast<string *>(gen->GetArgObject(0));
string * self = static_cast<string *>(gen->GetObject());
*self = *a;
gen->SetReturnAddress(self);
}
static void AddAssignStringGeneric(asIScriptGeneric *gen) {
string * a = static_cast<string *>(gen->GetArgObject(0));
string * self = static_cast<string *>(gen->GetObject());
*self += *a;
gen->SetReturnAddress(self);
}
static void StringEqualsGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
string * b = static_cast<string *>(gen->GetArgAddress(0));
*(bool*)gen->GetAddressOfReturnLocation() = (*a == *b);
}
static void StringCmpGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
string * b = static_cast<string *>(gen->GetArgAddress(0));
int cmp = 0;
if( *a < *b ) cmp = -1;
else if( *a > *b ) cmp = 1;
*(int*)gen->GetAddressOfReturnLocation() = cmp;
}
static void StringAddGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
string * b = static_cast<string *>(gen->GetArgAddress(0));
string ret_val = *a + *b;
gen->SetReturnObject(&ret_val);
}
static void StringLengthGeneric(asIScriptGeneric * gen) {
string * self = static_cast<string *>(gen->GetObject());
*static_cast<asUINT *>(gen->GetAddressOfReturnLocation()) = (asUINT)self->length();
}
static void StringResizeGeneric(asIScriptGeneric * gen) {
string * self = static_cast<string *>(gen->GetObject());
self->resize(*static_cast<asUINT *>(gen->GetAddressOfArg(0)));
}
static void StringCharAtGeneric(asIScriptGeneric * gen) {
unsigned int index = gen->GetArgDWord(0);
string * self = static_cast<string *>(gen->GetObject());
if (index >= self->size()) {
// Set a script exception
asIScriptContext *ctx = asGetActiveContext();
ctx->SetException("Out of range");
gen->SetReturnAddress(0);
} else {
gen->SetReturnAddress(&(self->operator [](index)));
}
}
static void AssignInt2StringGeneric(asIScriptGeneric *gen)
{
int *a = static_cast<int*>(gen->GetAddressOfArg(0));
string *self = static_cast<string*>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self = sstr.str();
gen->SetReturnAddress(self);
}
static void AssignUInt2StringGeneric(asIScriptGeneric *gen)
{
unsigned int *a = static_cast<unsigned int*>(gen->GetAddressOfArg(0));
string *self = static_cast<string*>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self = sstr.str();
gen->SetReturnAddress(self);
}
static void AssignDouble2StringGeneric(asIScriptGeneric *gen)
{
double *a = static_cast<double*>(gen->GetAddressOfArg(0));
string *self = static_cast<string*>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self = sstr.str();
gen->SetReturnAddress(self);
}
static void AssignBool2StringGeneric(asIScriptGeneric *gen)
{
bool *a = static_cast<bool*>(gen->GetAddressOfArg(0));
string *self = static_cast<string*>(gen->GetObject());
std::stringstream sstr;
sstr << (*a ? "true" : "false");
*self = sstr.str();
gen->SetReturnAddress(self);
}
static void AddAssignDouble2StringGeneric(asIScriptGeneric * gen) {
double * a = static_cast<double *>(gen->GetAddressOfArg(0));
string * self = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self += sstr.str();
gen->SetReturnAddress(self);
}
static void AddAssignInt2StringGeneric(asIScriptGeneric * gen) {
int * a = static_cast<int *>(gen->GetAddressOfArg(0));
string * self = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self += sstr.str();
gen->SetReturnAddress(self);
}
static void AddAssignUInt2StringGeneric(asIScriptGeneric * gen) {
unsigned int * a = static_cast<unsigned int *>(gen->GetAddressOfArg(0));
string * self = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a;
*self += sstr.str();
gen->SetReturnAddress(self);
}
static void AddAssignBool2StringGeneric(asIScriptGeneric * gen) {
bool * a = static_cast<bool *>(gen->GetAddressOfArg(0));
string * self = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << (*a ? "true" : "false");
*self += sstr.str();
gen->SetReturnAddress(self);
}
static void AddString2DoubleGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
double * b = static_cast<double *>(gen->GetAddressOfArg(0));
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddString2IntGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
int * b = static_cast<int *>(gen->GetAddressOfArg(0));
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddString2UIntGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
unsigned int * b = static_cast<unsigned int *>(gen->GetAddressOfArg(0));
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddString2BoolGeneric(asIScriptGeneric * gen) {
string * a = static_cast<string *>(gen->GetObject());
bool * b = static_cast<bool *>(gen->GetAddressOfArg(0));
std::stringstream sstr;
sstr << *a << (*b ? "true" : "false");
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddDouble2StringGeneric(asIScriptGeneric * gen) {
double* a = static_cast<double *>(gen->GetAddressOfArg(0));
string * b = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddInt2StringGeneric(asIScriptGeneric * gen) {
int* a = static_cast<int *>(gen->GetAddressOfArg(0));
string * b = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddUInt2StringGeneric(asIScriptGeneric * gen) {
unsigned int* a = static_cast<unsigned int *>(gen->GetAddressOfArg(0));
string * b = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << *a << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
static void AddBool2StringGeneric(asIScriptGeneric * gen) {
bool* a = static_cast<bool *>(gen->GetAddressOfArg(0));
string * b = static_cast<string *>(gen->GetObject());
std::stringstream sstr;
sstr << (*a ? "true" : "false") << *b;
std::string ret_val = sstr.str();
gen->SetReturnObject(&ret_val);
}
// This function returns a string containing the substring of the input string
// determined by the starting index and count of characters.
//
// AngelScript signature:
// string string::substr(uint start = 0, int count = -1) const
static void StringSubString_Generic(asIScriptGeneric *gen)
{
// Get the arguments
string *str = (string*)gen->GetObject();
asUINT start = *(int*)gen->GetAddressOfArg(0);
int count = *(int*)gen->GetAddressOfArg(1);
// Check for out-of-bounds
string ret;
if( start < str->length() && count != 0 )
ret = str->substr(start, count);
// Return the substring
new(gen->GetAddressOfReturnLocation()) string(ret);
}
void RegisterStdString_Generic(asIScriptEngine *engine)
{
int r;
// Register the string type
r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
// Register the string factory
r = engine->RegisterStringFactory("string", asFUNCTION(StringFactoryGeneric), asCALL_GENERIC); assert( r >= 0 );
// Register the object operator overloads
r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(CopyConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructStringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asFUNCTION(AssignStringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringEqualsGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmpGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTION(StringAddGeneric), asCALL_GENERIC); assert( r >= 0 );
// Register the object methods
r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLengthGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResizeGeneric), asCALL_GENERIC); assert( r >= 0 );
// Register the index operator, both as a mutator and as an inspector
r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 );
// Automatic conversion from values
r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddString2DoubleGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(int) const", asFUNCTION(AddString2IntGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(int) const", asFUNCTION(AddInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(uint) const", asFUNCTION(AddString2UIntGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(uint) const", asFUNCTION(AddUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddString2BoolGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString_Generic), asCALL_GENERIC); assert( r >= 0 );
}
static string StringFactory(asUINT length, const char *s)
{
return string(s, length);
}
static void ConstructString(string *thisPointer)
{
new(thisPointer) string();
}
static void CopyConstructString(const string &other, string *thisPointer)
{
new(thisPointer) string(other);
}
static void DestructString(string *thisPointer)
{
thisPointer->~string();
}
static string &AssignUIntToString(unsigned int i, string &dest)
{
ostringstream stream;
stream << i;
dest = stream.str();
return dest;
}
static string &AddAssignUIntToString(unsigned int i, string &dest)
{
ostringstream stream;
stream << i;
dest += stream.str();
return dest;
}
static string AddStringUInt(const string &str, unsigned int i)
{
ostringstream stream;
stream << i;
return str + stream.str();
}
static string AddStringUInt64(const string &str, unsigned long long i)
{
ostringstream stream;
stream << i;
return str + stream.str();
}
static string AddIntString(int i, const string &str)
{
ostringstream stream;
stream << i;
return stream.str() + str;
}
static string AddInt64String(long long i, const string &str)
{
ostringstream stream;
stream << i;
return stream.str() + str;
}
static string &AssignIntToString(int i, string &dest)
{
ostringstream stream;
stream << i;
dest = stream.str();
return dest;
}
static string &AddAssignIntToString(int i, string &dest)
{
ostringstream stream;
stream << i;
dest += stream.str();
return dest;
}
static string AddStringInt(const string &str, int i)
{
ostringstream stream;
stream << i;
return str + stream.str();
}
static string AddStringInt64(const string &str, long long i)
{
ostringstream stream;
stream << i;
return str + stream.str();
}
static string AddUIntString(unsigned int i, const string &str)
{
ostringstream stream;
stream << i;
return stream.str() + str;
}
static string AddUInt64String(unsigned long long i, const string &str)
{
ostringstream stream;
stream << i;
return stream.str() + str;
}
static string &AssignDoubleToString(double f, string &dest)
{
ostringstream stream;
stream << f;
dest = stream.str();
return dest;
}
static string &AddAssignDoubleToString(double f, string &dest)
{
ostringstream stream;
stream << f;
dest += stream.str();
return dest;
}
static string &AssignBoolToString(bool b, string &dest)
{
ostringstream stream;
stream << (b ? "true" : "false");
dest = stream.str();
return dest;
}
static string &AddAssignBoolToString(bool b, string &dest)
{
ostringstream stream;
stream << (b ? "true" : "false");
dest += stream.str();
return dest;
}
static string AddStringDouble(const string &str, double f)
{
ostringstream stream;
stream << f;
return str + stream.str();
}
static string AddDoubleString(double f, const string &str)
{
ostringstream stream;
stream << f;
return stream.str() + str;
}
static string AddStringBool(const string &str, bool b)
{
ostringstream stream;
stream << (b ? "true" : "false");
return str + stream.str();
}
static string AddBoolString(bool b, const string &str)
{
ostringstream stream;
stream << (b ? "true" : "false");
return stream.str() + str;
}
static char *StringCharAt(unsigned int i, string &str)
{
if( i >= str.size() )
{
// Set a script exception
asIScriptContext *ctx = asGetActiveContext();
ctx->SetException("Out of range");
// Return a null pointer
return 0;
}
return &str[i];
}
// AngelScript signature:
// int string::opCmp(const string &in) const
static int StringCmp(const string &a, const string &b)
{
int cmp = 0;
if( a < b ) cmp = -1;
else if( a > b ) cmp = 1;
return cmp;
}
static bool StringEq(const string &a, const string &b) {
return a == b;
}
// This function returns the index of the first position where the substring
// exists in the input string. If the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findFirst(const string &in sub, uint start = 0) const
static int StringFindFirst(const string &sub, asUINT start, const string &str)
{
// We don't register the method directly because the argument types change between 32bit and 64bit platforms
return (int)str.find(sub, start);
}
// This function returns the index of the last position where the substring
// exists in the input string. If the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findLast(const string &in sub, int start = -1) const
static int StringFindLast(const string &sub, int start, const string &str)
{
// We don't register the method directly because the argument types change between 32bit and 64bit platforms
return (int)str.rfind(sub, (size_t)start);
}
// AngelScript signature:
// uint string::length() const
static asUINT StringLength(const string &str)
{
// We don't register the method directly because the return type changes between 32bit and 64bit platforms
return (asUINT)str.length();
}
// AngelScript signature:
// void string::resize(uint l)
static void StringResize(asUINT l, string &str)
{
// We don't register the method directly because the argument types change between 32bit and 64bit platforms
str.resize(l);
}
void RegisterStdString_Native(asIScriptEngine *engine)
{
int r;
// Register the string type
r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
// Register the string factory
r = engine->RegisterStringFactory("string", asFUNCTION(StringFactory), asCALL_CDECL); assert( r >= 0 );
// Register the object operator overloads
r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(CopyConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asMETHODPR(string, operator =, (const string&), string&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asMETHODPR(string, operator+=, (const string&), string&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringEq), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTIONPR(operator +, (const string &, const string &), string), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
// Register the object methods
r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "uint get_length() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); assert( r >= 0 );
// Register the index operator, both as a mutator and as an inspector
// Note that we don't register the operator[] directory, as it doesn't do bounds checking
r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );
// Automatic conversion from values
r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddStringInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddStringUInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddStringBool), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBoolString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); assert( r >= 0 );
// TODO: Implement the following
// findFirstOf
// findLastOf
// findFirstNotOf
// findLastNotOf
// parseInt
// parseFloat
// formatInt - maybe as string::string(int64 value, const string &in format)
// formatFloat
// replace - replaces a text found in the string
// replaceRange - replaces a range of bytes in the string
// trim
// multiply/times - takes the string and multiplies it n times, e.g. "-".multiply(5) returns "-----"
}
void RegisterStdString(asIScriptEngine * engine)
{
if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY"))
RegisterStdString_Generic(engine);
else
RegisterStdString_Native(engine);
}
END_AS_NAMESPACE
@@ -0,0 +1,113 @@
#include <assert.h>
#include "scriptstdstring.h"
#include "scriptarray.h"
using namespace std;
BEGIN_AS_NAMESPACE
// This function takes an input string and splits it into parts by looking
// for a specified delimiter. Example:
//
// string str = "A|B||D";
// array<string>@ array = str.split("|");
//
// The resulting array has the following elements:
//
// {"A", "B", "", "D"}
//
// AngelScript signature:
// array<string>@ string::split(const string &in delim) const
static void StringSplit_Generic(asIScriptGeneric *gen)
{
// Obtain a pointer to the engine
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
// TODO: This should only be done once
// TODO: This assumes that CScriptArray was already registered
asITypeInfo *arrayType = engine->GetTypeInfoById(engine->GetTypeIdByDecl("array<string>"));
// Create the array object
CScriptArray *array = new CScriptArray(0, arrayType);
// Get the arguments
string *str = (string*)gen->GetObject();
string *delim = *(string**)gen->GetAddressOfArg(0);
// Find the existence of the delimiter in the input string
int pos = 0, prev = 0, count = 0;
while( (pos = (int)str->find(*delim, prev)) != (int)string::npos )
{
// Add the part to the array
array->Resize(array->GetSize()+1);
((string*)array->At(count))->assign(&(*str)[prev], pos-prev);
// Find the next part
count++;
prev = pos + (int)delim->length();
}
// Add the remaining part
array->Resize(array->GetSize()+1);
((string*)array->At(count))->assign(&(*str)[prev]);
// Return the array by handle
*(CScriptArray**)gen->GetAddressOfReturnLocation() = array;
}
// This function takes as input an array of string handles as well as a
// delimiter and concatenates the array elements into one delimited string.
// Example:
//
// array<string> array = {"A", "B", "", "D"};
// string str = join(array, "|");
//
// The resulting string is:
//
// "A|B||D"
//
// AngelScript signature:
// string join(const array<string> &in array, const string &in delim)
static void StringJoin_Generic(asIScriptGeneric *gen)
{
// Get the arguments
CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0);
string *delim = *(string**)gen->GetAddressOfArg(1);
// Create the new string
string str = "";
if( array->GetSize() )
{
int n;
for( n = 0; n < (int)array->GetSize() - 1; n++ )
{
str += *(string*)array->At(n);
str += *delim;
}
// Add the last part
str += *(string*)array->At(n);
}
// Return the string
new(gen->GetAddressOfReturnLocation()) string(str);
}
// This is where the utility functions are registered.
// The string type must have been registered first.
void RegisterStdStringUtils(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0);
}
END_AS_NAMESPACE