Problem in array<interface@>

Recommended Posts

Hello!

I'm getting an exception on executing this code:

interface IItem {
bool opEquals(const IItem@ &in other) const;
}

class CItem : CBase, IItem {
bool opEquals(const IELCollectionItem@ &in other) const {
return @this is @other;
}
}

class CHolder : CBase {
private array<IItem@> m_arrItems;

int32 indexOf(const IItem@ hItem) const {
if(@hItem !is null) {
return m_arrItems.find(@hItem); // <<<<< An exception is here
} else {
return -1;
}
}
}


The text of exception is "Type 'IItem' does not have a matching opEquals or opCmp method"

So in m_arrItems I have CItem handles, but an array itself is created to hold IItem handles.

Compiler says that all is OK but on execution it fails.

Share this post

Share on other sites

Thanks for letting me know about this. I will investigate it.

I suspect it can be worked around by changing the signature of opEquals to the following:

interface IItem {
bool opEquals(const IItem & other) const;
}


That is, take the IItem by reference, rather than a reference to a handle.

Note, the problem is only identified at run-time, because the compiler doesn't know that the registered array type will try to call the opEquals method. I already have an item on my to-do list to add more compile time callbacks to allow things like this to be verified at compile time.

Share this post

Share on other sites

Hello Andreas!

I checked the solution you suggested.

So I changed the signature in IItem and then changed the method which actually does all the work:

class CItem : CBase, IItem {
bool opEquals(const IItem & other) const {
return @this is @other;
}
}

But at runtime I'm now getting an Access Violation in

void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s) const


with the call stack

asCScriptEngine::CallObjectMethod
...
CScriptArray::equals
...
CScriptArray::find
...


Maybe the reason of AV is that it's impossible to get the handle of other because it's passed to the function as an interface?

Actually it's necessary to write code like the one from my first post only because there is no default implementation of opEquals for any class type in the language.

It would be good to have something like "reference equals" as the default for opEquals (like in C# for example).

This would also allow to put an instance of any class to array without implementing opEquals.

Share this post

Share on other sites

The "reference equals" operator in AngelScript is the "is" operator. Use the array method findByRef to search an array by reference instead of by value.

Share this post

Share on other sites

The "reference equals" operator in AngelScript is the "is" operator. Use the array method findByRef to search an array by reference instead of by value.

Yes, I know that it's possible to do so, but for array containing handles it also doesn't work.

Simple example:

interface IItem {
}

class CItem : IItem {
}

array<IItem@> items;

int32 getIndexOf(const IItem@ &in hItem) {
return items.findByRef(@hItem);
}

void check() {
CItem@ hItem = CItem();
items.insertLast(@hItem);
println("Index from getIndexOf(): " + getIndexOf(@hItem));   // prints -1
println("Index from findByRef: " + items.findByRef(@hItem)); // prints 0
}

So when we pass hItem to getIndexOf as const IItem@ the findByRef returns -1 for the object which is in array.

I need to implement a member function with signature like int32 getIndexOf(const IItem@ &in hItem) and this is not possible

using my code from topic start nor the code suggested by Andreas, nor the code with findByRef suggested by Sir Ementaler.

Share this post

Share on other sites

That appears to be some sort of a side effect, possibly a bug, of passing the handle by non-const &in reference. There is no good reason whatsoever to pass handles by &in reference. A signature such as "int32 getIndexOf(const IItem@ hItem)" should work correctly.

Edited by Sir Ementaler

Share this post

Share on other sites

I've identified a bug in the script compiler when the function argument is IItem@ &in and it is passed on to another function. In this case the compiler is not correctly dereferencing the argument.

I'm working on the fix.

In the meantime you should be able to make it work without the &in, which as Sir Ementaler correctly mentioned, is not really necessary for @ arguments.

Share this post

Share on other sites

I've fixed the bug in revision 2347.

Regards,

Andreas

Share this post

Share on other sites

It would be good to have something like "reference equals" as the default for opEquals (like in C# for example). This would also allow to put an instance of any class to array without implementing opEquals.

Thanks for the suggestion. I'll keep it in mind for a possible future enhancement.

Share this post

Share on other sites

Hello Sir Ementaler!

That appears to be some sort of a side effect, possibly a bug, of passing the handle by non-const &in reference. There is no good reason whatsoever to pass handles by &in reference. A signature such as "int32 getIndexOf(const IItem@ hItem)" should work correctly.

Yes I agree on 100% that it's unnecessary to pass handles by &in reference.

But such signature is a part of ScriptArray addon API: when we create a variable of

type array<IItem@> the find method

(whose signature is int find(const T&in if_handle_then_const value) const)

will actually have the find(const IItem@ &in if_handle_then_const value) const signature.

It seems there is no any method to avoid such a situation in array<>.

So I just simulated this logic in my simple example above to show the problem.

Hello Andreas!

I've fixed the bug in revision 2347.

Regards,

Andreas

I recompiled the library and all works perfect!

Thank you very much!

Your support is really better than the "paid support" in some companies. :)

Edited by IronHawk

Share this post

Share on other sites

Yes I agree on 100% that it's unnecessary to pass handles by &in reference.

But such signature is a part of ScriptArray addon API: when we create a variable of

type array<IItem@> the find method

(whose signature is int find(const T&in if_handle_then_const value) const)

will actually have the find(const IItem@ &in if_handle_then_const value) const signature.

It seems there is no any method to avoid such a situation in array<>.

To my information, that is incorrect. If you attempt to query the signature of the method, it's actually "int array::find(Item@const&in value) const" (or likely "int array::find(const Item@const&in value) const" in the WIP version I am not using). Notice the const qualifier in front of "&in", meaning that the handle should be passed by const &in reference rather than just &in, and thus it's possible to avoid creating a copy of the handle. The same signature would not be considered valid in other contexts, i.e. outside of templates. As such, outside of templates it is unnecessary and actually somewhat inefficient to use the signature you're using.

Edited by Sir Ementaler

Share this post

Share on other sites

Yes I agree on 100% that it's unnecessary to pass handles by &in reference.

But such signature is a part of ScriptArray addon API: when we create a variable of

type array<IItem@> the find method

(whose signature is int find(const T&in if_handle_then_const value) const)

will actually have the find(const IItem@ &in if_handle_then_const value) const signature.

It seems there is no any method to avoid such a situation in array<>.

To my information, that is incorrect. If you attempt to query the signature of the method, it's actually "int array::find(Garland@const&in value) const" (or likely "int array::find(const Garland@const&in value) const" in the WIP version I am not using). Notice the const qualifier in front of "&in", meaning that the handle should be passed by const &in reference rather than just &in, and thus it's possible to avoid creating a copy of the handle. The same signature would not be considered valid in other contexts, i.e. outside of templates. As such, outside of templates it is unnecessary and actually somewhat inefficient to use the signature you're using.

Checked again 5 min ago:

asITypeInfo *ti = engine->GetTypeInfoByDecl("array<T>");
asUINT countMethods = ti->GetMethodCount();
for (asUINT i = 0; i < countMethods; i++) {
asIScriptFunction *func = ti->GetMethodByIndex(i);
const char *decl = func->GetDeclaration();
...

And the result is:

"int array::find(const T&in) const"

Version 2.31.2 WIP with last fixes.

Share this post

Share on other sites

Yes, but that doesn't relate at all to what I claimed. Notice how in C++ too, the signature "int find(const T&)" for T = int* would become "int find(int* const&)" and not "int find(const int*&)". Instantiation of a template is more than just replacing every "T" with the type name. You should be checking the signature for the type array<Item@> if you want to determine anything.

Share this post

Share on other sites

Yes, but that doesn't relate at all to what I claimed. Notice how in C++ too, the signature "int find(const T&)" for T = int* would become "int find(int* const&)" and not "int find(const int*&)". Instantiation of a template is more than just replacing every "T" with the type name. You should be checking the signature for the type array<Item@> if you want to determine anything.

Yes, but for variable like "array<IItem@> items" the GetGlobalVarDeclaration(...) returns "IItem@[] items" and etc.

So I think it's necessary to ask an AS author (hi, Andreas :)) if internally the instantiation works same way as in C++.

Share this post

Share on other sites

It works similarly to C++.

int find(const T&in if_handle_then_const value) const


becomes

int find(const IItem @const &in value) const


for array<IItem@>. The keyword if_handle_then_const is what tells AngelScript to add the first const, otherwise it would be exactly as in C++, i.e. find(IItem @const &in value).

Observe, IItem@[] is an alias for array<IItem@> (if you've registered array<T> as the default array type).

The signature find(const IItem @const &in value) const is not really needed for normal script functions, as the same result can be accomplished with find(const IItem @ value). The only reason for the template instance to create the function with the first signature rather than the latter, is because the C++ implementation is the same regardless of the instantiated type, so the function signature must obey the registered signature, i.e. take a const reference to whatever subtype the template is instantiated for.

Share this post

Share on other sites

It works similarly to C++.

...

Thank you, Andreas!

Thank you for discussion, Sir Ementaler!

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

• Partner Spotlight

• Forum Statistics

• Total Topics
627636
• Total Posts
2978319

• 10
• 12
• 22
• 13
• 33