Advertisement

Get asIObjectType of built-in type

Started by September 13, 2014 11:28 AM
6 comments, last by WitchLord 10 years, 2 months ago

Hello, I'm new to angelscript library. I try to make such task as passing array from application to script using scriptarray add-on. So I suppose I need this


static CScriptArray *Create(asIObjectType *ot);

to create array on application side.

But I can't get asIObjectType for built-in types.

That's what I do (minimized example):


#include <angelscript.h>
#include <cstdio>

int main()
{
    asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
    int intTypeId = engine->GetTypeIdByDecl("int");
    
    if (intTypeId < 0)
    {
        printf("Type id is invalid\n");
    }
    else
    {
        asIObjectType* intType = engine->GetObjectTypeById(intTypeId);
        if (!intType)
        {
            printf("Can't retrieve type\n");
        }
    }
    engine->Release();
    return 0;
}

While it can retrieve type id with GetTypeIdByDecl, but GetObjectTypeById returns null.

I use 2.29.0 version of angelscript.

Primitive types are not object types, so it is not possible to get an object type for them. However, you don't need it. To construct the array you need the object type of the array itself, not the elements.

See the example from the manual:

// Registered with AngelScript as 'array<string> @CreateArrayOfString()'
CScriptArray *CreateArrayOfStrings()
{
  // If called from the script, there will always be an active
  // context, which can be used to obtain a pointer to the engine.
  asIScriptContext *ctx = asGetActiveContext();
  if( ctx )
  {
    asIScriptEngine* engine = ctx->GetEngine();
 
    // The script array needs to know its type to properly handle the elements.
    // Note that the object type should be cached to avoid performance issues
    // if the function is called frequently.
    asIObjectType* t = engine->GetObjectTypeByDecl("array<string>");
 
    // Create an array with the initial size of 3 elements
    CScriptArray* arr = CScriptArray::Create(t, 3);
    for( asUINT i = 0; i < arr->GetSize(); i++ )
    {
      // Set the value of each element
      string val("test");
      arr->SetValue(i, &val);
    }
 
    // The ref count for the returned handle was already set in the array's constructor
    return arr;
  }
  return 0;
}

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

Advertisement

Thanks, I missed manual somehow. But now I got segmentation fault if I register created array as


array<string>@ myArr

but this works:


array<string> myArr

The full code snippet:


#include <angelscript.h>
#include "scriptstdstring/scriptstdstring.h"
#include "scriptbuilder/scriptbuilder.h"
#include "scriptarray/scriptarray.h"
#include <cstdio>
#include <string>
#include <cassert>

CScriptArray *CreateArrayOfStrings(asIScriptEngine* engine)
{
    asIObjectType* t = engine->GetObjectTypeByDecl("array<string>");
    assert(t);
    CScriptArray* arr = CScriptArray::Create(t, 3);
    assert(arr);
    for( asUINT i = 0; i < arr->GetSize(); i++ )
    {
        std::string val("test");
        arr->SetValue(i, &val);
    }
    return arr;
}

void writeln(const std::string &msg)
{
    fprintf(stderr, "%s\n", msg.c_str());
}

void MessageCallback(const asSMessageInfo *msg, void *param)
{
    const char *type = "ERR ";
    if( msg->type == asMSGTYPE_WARNING ) 
        type = "WARN";
    else if( msg->type == asMSGTYPE_INFORMATION ) 
        type = "INFO";
    printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

int main()
{
    asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
    int r = engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); assert( r >= 0 );
    
    RegisterStdString(engine);
    RegisterScriptArray(engine, true);
    r = engine->RegisterGlobalFunction("void writeln(const string &in)", asFUNCTION(writeln), asCALL_CDECL); assert( r >= 0 );
    
    CScriptArray* myArr = CreateArrayOfStrings(engine);
    r = engine->RegisterGlobalProperty("array<string>@ myArr", myArr); assert(r >= 0);
    
    CScriptBuilder builder;
    r = builder.StartNewModule(engine, "MyModule"); 
    if( r < 0 ) 
    {
        printf("Unrecoverable error while starting a new module.\n");
        return 1;
    }
    r = builder.AddSectionFromFile("test.as");
    if( r < 0 )
    {
        printf("Please correct the errors in the script and try again.\n");
        return 1;
    }
    r = builder.BuildModule();
    if( r < 0 )
    {
        printf("Please correct the errors in the script and try again.\n");
        return 1;
    }
    
    asIScriptModule *mod = engine->GetModule("MyModule");
    asIScriptFunction *func = mod->GetFunctionByDecl("void main()");
    if( func == 0 )
    {
        printf("The script must have the function 'void main()'. Please add it and try again.\n");
        return 1;
    }
    
    asIScriptContext *ctx = engine->CreateContext();
    
    ctx->Prepare(func);
    r = ctx->Execute();
    if( r != asEXECUTION_FINISHED )
    {
        // The execution didn't complete as expected. Determine what happened.
        if( r == asEXECUTION_EXCEPTION )
        {
            // An exception occurred, let the script writer know what happened so it can be corrected.
            printf("An exception '%s' occurred. Please correct the code and try again.\n", ctx->GetExceptionString());
        }
    }
    
    ctx->Release();
    myArr->Release();
    engine->Release();
    return 0;
} 

Script code:


void main()
{
    for( uint n = 0; n < myArr.length(); n++ )
        writeln(myArr[n]);
}
 

I got seg fault in


asUINT CScriptArray::GetSize() const
{
	return buffer->numElements;
} 

Is it intended behavior?

.


CScriptArray* myArr = CreateArrayOfStrings(engine);
r = engine->RegisterGlobalProperty("array<string>@ myArr", myArr); assert(r >= 0);

You're registering the array with incorrect level of indirection.

When the declaration is a @ (handle) the second argument must be an address of a pointer, i.e:

CScriptArray* myArr = CreateArrayOfStrings(engine);
r = engine->RegisterGlobalProperty("array<string>@ myArr", &myArr); assert(r >= 0);

If the declaration is not a @, then the second argument should be the address to the actual object/value, e.g:

CScriptArray* myArr = CreateArrayOfStrings(engine);
r = engine->RegisterGlobalProperty("array<string> myArr", myArr); assert(r >= 0);
 

Unfortunately there is no way for AngelScript to detect the wrong level of indirection in the registration, so the application developer will need to pay extra attention here.

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

Oh, comment in the example confused me. I mean this:


// Registered with AngelScript as 'array<string> @CreateArrayOfString()'

It made me think that pointer is already 'handle' because of '@' and that I don't need to pass reference to pointer, but just pointer.

A handle in AngelScript is basically the same as a pointer in C++. The indirection level is the same, as in a handle refers to an object, in the same way that a pointer refers to an object. The difference is that the handle will automatically increment/decrement the reference count to the object, and a handle can only refer to valid objects or null.

I guess the confusion is with RegisterGlobalProperty itself (you're not the first one to get confused with how it works). It has to get an address to the value of the declared type, so the script can see the same value that the application sees even if the application later changes that value. That's why when registering a global property as a handle you have to give the address of the handle, and not the handle itself.

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

Thanks, I think, I got it. I always should pass pointer to type, even if type itself is kind of pointer (like handle). This is logical.

But I still need clarification about these cases:


MyClass a;
MyClass@ b;
@b = a;

and


MyClass a;
MyClass@ b;
@b = @a;

Is there some semantics difference?

The two have the same effect.

In the first case the compiler implicitly takes the address of a, and in the second case it is done explicitly by the script writer.

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

This topic is closed to new replies.

Advertisement