Advertisement

Determining Template Subtype in Factory

Started by September 07, 2011 07:02 AM
3 comments, last by BrianEmpson 13 years, 5 months ago
Hello,

I'm having trouble finding out what subtype is being used in my script templates when they are not primitive types, like an application class object, etc...

Here is the registrations for the template:


//The array type
r = engine->RegisterObjectType("array<T>",0,asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0);

//Factories and callbacks
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in)", asFUNCTIONPR(IrrArrayFactory, (asIObjectType*), void), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in, u32)", asFUNCTIONPR(IrrArrayFactory2, (asIObjectType*, u32), void), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in, const array<T> &in)", asFUNCTIONPR(IrrArrayFactory3, (asIObjectType*, const array<class T>), void), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(TemplateCallback), asCALL_CDECL); assert( r >= 0 );

//These are just to get the type to register properly for testing...I will implement a proper wrapper or something later
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ADDREF, "void f()", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASE, "void f()", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(_NullFunc), asCALL_CDECL_OBJLAST); assert( r >= 0 );

//The factory function itself
void IrrArrayFactory(asIObjectType *ot) //This is void for testing, later it will return an array
{
s32 typeId = ot->GetSubTypeId(); //s32 is just an int typedef
stringc typeName = ot->GetName(); //stringc is my 8 bit char string class (in contrast to 16 bit stringw)
cout<<"IrrArrayFactory()\n";
cout<<"Object: "<<typeName.c_str()<<" Type: "<<typeIdToStr(typeId)<<"\n";

return;
}


Because I do not know the template subtype yet, I can't instance an array<T> object yet. So I planned on doing something like this in the factory functions:


switch (subtype) {
case stringc:
array *a = new array<stringc>(/* params */);
case etc...:
//continue for rest of defined types
}


But that's not too flexible...is there a way to find the type of the object in the application and dynamically instance a type in c++? Or am I going about this in the wrong way?

As a side note, I wrote a function to find the subtype id name as such:


char* typeIdToStr(u32 id)
{
cout<<"id was: "<<hex<<id<<" \n";
switch (id) {
case asTYPEID_VOID:
return "void";
case asTYPEID_BOOL:
return "bool";
case asTYPEID_INT8:
return "int8";
case asTYPEID_INT16:
return "int16";
case asTYPEID_INT32:
return "int32";
case asTYPEID_INT64:
return "int64";
case asTYPEID_UINT8:
return "uint8";
case asTYPEID_UINT16:
return "uint16";
case asTYPEID_UINT32:
return "uint32";
case asTYPEID_UINT64:
return "uint64";
case asTYPEID_FLOAT:
return "float";
case asTYPEID_DOUBLE:
return "double";
case asTYPEID_OBJHANDLE:
return "objhandle";
case asTYPEID_HANDLETOCONST:
return "handletoconst";
case asTYPEID_MASK_OBJECT:
return "mask_object";
case asTYPEID_APPOBJECT:
return "appobject";
case asTYPEID_SCRIPTOBJECT:
return "scriptobject";
case asTYPEID_TEMPLATE:
return "template";
case asTYPEID_MASK_SEQNBR:
return "mask_seqnbr";
default:
return "unknown";
}
}


However, when executing the following script that makes an array of stringc's (an application registered string class), it returns:


IrrArrayFactory()
id was: 67108892
Object: array Type: unknown


Test Script:

print("Testing array object stuff...\n");
array<stringc> the_strings; //Don't actually do anything with it...there's no real factory yet...


Does this indicate the the array subtype is invalid? It didn't throw an error like I would expect, nor is it any of the predefined values. (Note that this works fine for primitives):


print("Testing array object stuff...\n");
array<u32> the_strings;


Result:


IrrArrayFactory()
id was: 8
Object: array Type: uint32
The typeId is actually a bitmask, and not a simple sequencial id. Only the lower bits identified by asTYPEID_MASK_SEQNBR are sequencial, where there will be one unique number for each base type.

You identify an application object by doing the following:



if( typeId & asTYPEID_APPOBJECT )
{
// This is an app object

if( typeId & asTYPEID_OBJHANDLE )
{
// This is a handle to an app object
}
}



However, I don't think you will be able to register the irrlicht array like this. You have to understand that two C++ template instances are two unique types, each with their own implementation specific for the subtype. The AngelScript template isn't really a unique implementation for each type, unless you register template specializations for each type.

A factory function cannot return different object types depending on the input. AngelScript wouldn't know how to deal with that (neither would C++ for that matter). At the very least the types must have a common base type, and the factory must return a pointer to the base type and not the derived type.






AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Thank you for clearing that up. So, I should create a wrapper class to deal with the instances of the array in the script, correct? I see in the scriptany add on that it's stored in a void* pointer but I do not quite understand what accounting needs to be done on the C++ side, especially if I need to transfer the array back and forth. (If I did need to do this, I would know the subtype beforehand as there aren't many instances where I would need to do this) So the factory would return a pointer to an object base that could handle storing the elements properly? Just trying to figure out the correct way to do this. This is new territory for me as I am not familiar with creating and using factories.
Rather than creating your own wrapper class, I suggest you use the CScriptArray add-on as the base. You can modify it to suit your purpose.

The add_on/scriptstdstring/scriptstdstring_utils.cpp file has a couple of functions that I think clarifies how the CScriptArray would be used from the C++ side when you need to transform the CScriptArray to the irrLicht array and vice versa.



If your application uses only a few array types that already known at compile time, you can register these directly as template specializations. It would be easier to wrap the irrlicht array that way, and you would also gain performance, as you wouldn't have to deal with runtime type identification.

A template specialization is registered just as an ordinary class, except that you use the name or the template with the subtype, e.g.:


// Register the array template from add_on/scriptarray/scriptarray.h
RegisterScriptArray(engine, true);

// Register a template specialization for the types that the application uses
r = engine->RegisterObjectType("array<float>", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<float>", asBEHAVE_FACTORY, "array<float> @f()", asFUNCTIONPR(FloatArray_Factory, (), FloatArray*), asCALL_CDECL); assert( r >= 0 );
...


// The factory function for the template specialization looks exactly like a factory for a normal type, since the subtype is already known at compile time
FloatArray *FloatArray_Factory()
{
return new FloatArray();
}



Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Hmm, it would seem to me that registering the container types from irrLicht is a waste of time. Angelscript implements array and a map-like container, and I'm assuming I can use array like a list and just convert these types to their proper Irrlicht types with a function somewhere. This seems like a much easier way to do things. It could be laziness, but another solution I opted out of was the store the array subtype as a parameter, feed it to a wrapper so that I could cast the void pointer of the object to the type for returning an array...but, I think this will be safer.

This topic is closed to new replies.

Advertisement