Jump to content

  • Log In with Google      Sign In   
  • Create Account

Andreas Jonsson

Member Since 26 Mar 2000
Offline Last Active Yesterday, 08:42 PM

#5096765 Why need to copy when using const T &in

Posted by Andreas Jonsson on 25 September 2013 - 01:59 PM

The real reason for &in and &out is to support sandboxing, which means that the script language must guarantee that references and handles are always valid.


In C++ it is quite easy to write code that invalidates a reference passed to a function. This is OK for C++ because the programmer is responsible for making sure everything is working ok, but with scripting it is not always possible to assume the script writer will be responsible (or even have a clear understanding over how things work).


// C++
vector<string> g_array;
void foo(string &value)
   value = "crash since the reference is no longer valid";
void main()


Within the script itself &in doesn't make much sense, since the end result is the same as passing the argument by value. But in order to maintain compatibility with C++ where many input arguments are passed by & for performance reason it is needed to differentiate between by value and input reference.


&out is needed as it is the only way a function can give additional return values.



Now, if you don't care about sand-boxing you can change the behaviour of AngelScript by setting the engine property asEP_ALLOW_UNSAFE_REFERENCES to true. When this is set you will be able to use &inout on all types, including primitives and registered value types, which makes it equivalent to C++.

#5096408 Unsigned int64 wokring wrong

Posted by Andreas Jonsson on 24 September 2013 - 08:06 AM

Hi Apmyp,


thanks for the bug report. I'll look into this and have it fixed as soon as I can.




#5095750 Bmfont outlined fonts

Posted by Andreas Jonsson on 21 September 2013 - 08:47 AM

Thanks for the compliments. 


I'll see if I can steal away some time from the AngelScript development to add these enhancements to BMFont. They have been on my to-do list for way too long already anyway.

#5095520 Calling overriden Mixin

Posted by Andreas Jonsson on 20 September 2013 - 11:22 AM

It's doable, but it would definitely not a trivial solution. I'll add a note on my to-do list to analyze this in detail for a possible future improvement.

#5095209 Script object reference in C++

Posted by Andreas Jonsson on 19 September 2013 - 10:56 AM

GetAddressOfVar() will return the address of the value in the variable. If the variable is a primitive type then the address will point to the value of the primitive, if the variable is a handle then the address will point to the handle, if the variable is local object (even if it is a ref type) then the address will point to the object.


You can use the typeId to know if the variable is a handle or not by checking for the bit asTYPEID_OBJHANDLE. If it is not a handle, then the variable will not be null.


I suggest you take a look at the CDebugger add-on in the SDK, particularly the ToString() and PrintValue() methods. It shows how to interpret the information you get about the variables.




#5094724 Register Parameter Names

Posted by Andreas Jonsson on 17 September 2013 - 01:22 PM

Yes, AngelScript doesn't store the name of the parameters for registered functions as it is not something that is needed by the engine.


You can use the user data in the function object to store the parameter names if you wish.




const char *decl = "void SetSize(int nRows, int nCols)";
r = engine->RegisterObjectMethod("Matrix", decl,
    asMETHODPR(Matrix, SetSize, (int,int), void), asCALL_THISCALL);
if( r > 0 )
   asIScriptFunction *func = engine->GetFunctionById(r);

#5094246 Ownership problem of Funcdefs.

Posted by Andreas Jonsson on 15 September 2013 - 10:16 AM

The asIScriptFunction object is the actual script code. As long as you don't discard the script module the function object will be valid. You do not need to increase the reference of the asIScriptFunction if you have control over when the script modules are discarded. If you want you can use the user data of the asIScriptFunction to receive a callback when the actual script function is being removed from the engine. With the callback you will then be able to cleanup any code that is still referencing the asIScriptFunction.







While I could theoretically change the built-in delegate object to use weak references it is currently not something I plan to do. Delegates can be used with application registered types as well, and not all application types are implemented to support weak references. 




#5094089 Ownership problem of Funcdefs.

Posted by Andreas Jonsson on 14 September 2013 - 04:07 PM

Hi Wracky,


If you don't want the delegate to hold a strong reference to the original object, then you'll need to look into implementing a weak-reference container.


Luckily for you, with version 2.27.1 weak references is already a reality, so it should be quite easy for you to do cool.png


I recommend you keep using the built-in delegates for allowing the script to pass the callback to the application, but instead of storing the received delegate directly, you dissect it and store the object pointer with a weak reference and the method pointer normally.


void FuncReceivingDelegate(asIScriptFunction *delegate)
   // Get the object pointer from the delegate
   if( delegate->GetFuncType() == asFUNC_DELEGATE )
     void *obj = delegate->GetDelegateObject();
     asIObjectType *objType = delegate->GetDelegateObjectType();
     asIScriptFunction *method = delegate->GetDelegateMethod();
     // Create a weak ref to keep the pointer to the object
     CScriptWeakRef *weakRefToDelegatedObject = new CScriptWeakRef(obj, objType);
     // Store the weak ref and the method instead of the original delegate


In case you wish to explore the generic funcdefs option, then you need to know that the generic handle add-on is capable of holding references to funcdefs too.




#5091630 Strange asOBJ_POD behavior with copy constructor and opAdd

Posted by Andreas Jonsson on 04 September 2013 - 02:24 PM

Whatever you do, do not try to trick to AngelScript. It will just cause difficult to find bugs as things don't quite work as it should in all cases. ph34r.png


Seriously, the asOBJ_APP_ flags must be registered correctly, as these are what AngelScript uses to know exactly how the host application expects to handle the type in the native calling convention. If you can, I recommend that you use the GetTypeTraits<T>() helper function. It will make sure you don't make a mistake in chosing the wrong flags.


A few flags cannot be determined automatically though, and these are asOBJ_APP_CLASS_ALLINTS, asOBJ_APP_CLASS_ALLFLOATS, and asOBJ_APP_CLASS_ALIGN8. On platforms that split value types in different CPU registers (e.g. 64bit Linux) these flags may be needed to tell AngelScript about the content of the type, so that it will be able to place the data in the correct CPU registers.


The manual explains what each flags means and how to use them.

#5091064 Strange asOBJ_POD behavior with copy constructor and opAdd

Posted by Andreas Jonsson on 02 September 2013 - 10:43 AM

It is all related to the native calling convention. In your first test you hadn't registered the Integer class correctly, which is why you were getting the wrong result. The original declaration should be registered with the flags asOBJ_APP_CLASS_C, not asOBJ_APP_CLASS_CK as it didn't have an explicit copy constructor.


When you explicitly declared the copy constructor in the Integer class the result was corrected as now the flag asOBJ_APP_CLASS_CK was correct.


If you're using a compiler with support for C++11 features (e.g. gnuc 4.7+, or MSVC 2012+), you can use the helper function GetTypeTraits<T>() to automatically determine the correct flags. For example:


#include <add_on/scripthelper/scripthelper.h>
r = _engine->RegisterObjectType("Integer", sizeof(Integer), asOBJ_VALUE | asOBJ_POD | GetTypeTraits<Integer>()); assert(r >= 0);


As for AngelScript calling or not calling the copy constructor, that is an optimization thing. There are still some places in the library where AngelScript will first create the object with the default constructor and then assign the value with the opAssign method. These are already on my to-do list to improve so that the copy constructor is called directly.



The btVector3 type has another trait that is not yet supported by AngelScript, and that is that the type is 16byte aligned (to optimize it for SIMD instructions). As such you cannot register this type as a value type. If you really want to use btVector3 directly in the scripts you need to register it as a scoped reference type, which will behave as a value type in the script, but is really allocated on the heap where it can be properly aligned to 16byte boundaries. Combine that with a memory pool for the allocation and deallocation of the vectors and you should not have any noticeable performance impacts.


Another option is that you avoid exposing the btVector3 type directly to the script, and instead use an ordinary unaligned structure for the vector3 type that the script will use. Of course, you will need to create wrappers for the functions that expect the btVector3 type, but hopefully you don't have too many of those exposed to the scripts.

#5090461 Reference Objects as Properties

Posted by Andreas Jonsson on 30 August 2013 - 03:37 PM

asOBJ_GC is necessary if the object can form circular references that will not be resolved by the application.


The classes TypeA, TypeB, and TypeC in your example can't form any circular references, 


If TypeA could hold a pointer to TypeC too, then a circular reference could be formed between TypeA and TypeC. If the application doesn't keep track of these and resolve them itself then the asOBJ_GC flag and related behaviours are necessary to avoid memory leaks.

#5090409 Reference Objects as Properties

Posted by Andreas Jonsson on 30 August 2013 - 11:39 AM

TypeB is complicated. TypeA is in this case used as a value type in C++ even though it is a reference type. Unless you implement the control to make sure TypeB stays alive as long as there are references to the member you'd do best not to expose this to the scripts at all. Instead I suggest that you use property accessors to allow the script to manipulate the TypeA member without directly accessing it.


For TypeC you're correct to use @. If you want to prevent that the pointer is reassigned, then ideally AngelScript should allow the member as &, but that is currently not supported. For now you'll need to use property accessors to make sure the script cannot reassign the handle. In this case you just need to expose a get accessor that returns the handle to the member.

#5088589 AngelScript 2.27.1 is here (so soon? yes)

Posted by Andreas Jonsson on 23 August 2013 - 10:02 PM

Maybe not the same syntax, but I definitely have plans to add support for variadic functions in the future.

#5088221 AngelScript 2.27.1 is here (so soon? yes)

Posted by Andreas Jonsson on 22 August 2013 - 04:56 PM

I'm working on improving the initialization lists. Not only will they be more efficient, but they will also be more versatile, and should allow AngelScript to be viable option for those looking for a script language to do a lot of data configuration.


Currently the initialization lists are very limited as the compiler is only able to use them via the index operator. It will then invoke the index operator for each element provided in the initialization list. This works well for ordinary arrays, but when you want something a little more complex, like a dictionary or grid of data, then the current solution doesn't work well at all.


With the new solution that I'm working on, the application will be able to define the pattern that the initialization list for a type must follow. The compiler will do the validation of this pattern at compile time, and will then provide a single buffer with all the data to the object constructor/factory. The object constructor can then parse this buffer to initialize the entire object before leaving.


The way the application will register the desired pattern will be something like this:


RegisterObjectBehaviour("array<T>", asBEHAVE_LIST_FACTORY, "array<T> @f() = {T repeat}", ...);

RegisterObjectBehaviour("dictionary", asBEHAVE_LIST_FACTORY, "dictionary @f() = {{string ?} repeat", ...);

RegisterObjectBehaviour("grid<T>", asBEHAVE_LIST_FACTORY, "grid<T> @f() = {{T repeat(x)} repeat}", ...);


For the array, the list will then be a list of values of the type T, e.g. {1, 2, 3, 4}

For the dictionary the list will be an array of key-value pairs, e.g. {{"car", @Car}, {"health", 23}}  

For the grid the list will be an array of arrays, where the compiler will guarantee that each row has the same amount of elements as the first one, e.g. {{0,1,2},{3,4,5},{6,7,8}}


I will take a while to have this implemented, and I may not be able to finish it all for version 2.28.0. For now I'm focusing on just implementing the way the buffer is initialized and then passed to the constructor. This may seem simple but I need to make sure the exception handler is capable of handling errors in the middle of the execution and also to have the offsets within the buffer adjusted properly when saving the bytecode to keep it platform independent.

#5086996 "global" and "static" module

Posted by Andreas Jonsson on 18 August 2013 - 04:02 AM

You can use function imports to accomplish this.


In your "static" modules add the following:


import void def_item_drop_behavior() from "global";


When the application compiles the "static" modules it must call the method BindAllImportedFunctions on the module after the build completes.


You may also want to read up on shared entities, with which you can share classes and interfaces between modules so you don't have to work with just primitives and registered types in the imported functions.