wacky behaviour when using out references (2.6.0 WIP 3)

Started by
12 comments, last by WitchLord 18 years, 1 month ago
hi to all, i'm new to this forum.. but is some time that i get into AngelScript, and i like that very much, Andreas, you done a excellent work! anyway i've started going into troubles when using some c methods that have references to be filled. i know already that we can use out references for this but the problem i found is somehow interesting. i have a class, RegularExpression, that have a method like this, nothing fancy but passing a index of the found string, it will fill out the indexes of that string inside the searched string. String getStringOffset( int num, int& start, int& end ); i've registered the type and method: e->RegisterObjectMethod("RegExp", "String getStringOffset(int, int& out, int& out)", asMETHODPR(RegularExpression,getStringOffsetSize), asCALL_THISCALL); but when i call this from the script i get strange behaviours on the first parameter passed (which is a index and set up by the script just before calling it): then in script i continue: void main() { RegExp re("\\d{3}"); int found = re.match("123.345"); if ( found >= 0 ) { String s = ""; int start; int end; for( int i=0; i<found; i++ ) { String result = re.getStringOffset(i,start,end); s += "'" + result + "' - (" + start + "," + end + ") \n" ; } warning("what i found",s); } } i found 2 strings offsets, so RegularExpression.matches() == 2, but nothing happens (in c++ is working nicely). if i debug and step into the (c++) function, i can see that the first call to getStringOffset, parameter i is not 0 as expected but 149419568, then returning always nothing at all (cause is out of bounds of regexp indexes). so i've made 3 functions for regexp (in c++) that the first get start offset, the second the end offset and the third the string, and none are using out references. then the indexes in the for loop are right. what i'm doing wrong here ?
Advertisement
It looks like the you're receiving the address of the memory where the string is to be returned in the i parameter (the value 149419568 looks like it could be a memory address).

I need to know how the String type is registered, and how it is declared in C++.

I'll make some tests as well, to verify if this is not a bug in the library.

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

actually i have a utf8 compliant String class in my framework. then i have modeled a wrapper (after scriptstring addon) that let me call my real string class. it is registered with all the operators, addref - release, various constructors... i've not the last one cause i'm @ work, but this may be useful

asString is a wrapped asString around my String class:

class asString
{
public:
asString();
asString(const asString &other);
asString(const char s);
asString(const char *s);
asString(const tchar s);
asString(const tchar *s);
asString(const String &s);

void addRef();
void release();

unsigned int length();

asString& operator=(const asString &other);
asString& operator+=(const asString &other);

static void registerObjectType(asIScriptEngine *engine);

String buffer;

protected:
~asString();
int refCount;
};





// This function allocates memory for the string object
static void *StringAlloc(int)
{
return new char[sizeof(asString)];
}

// This function deallocates the memory for the string object
static void StringFree(void *p)
{
jassert( p );
delete p;
}

// This is the string factory that creates new strings for the script
static asString *StringFactory(asUINT length, const char *s)
{
// Return a script handle to a new string
return new asString(s);
}

// This is a wrapper for the default asString constructor, since
// it is not possible to take the address of the constructor directly
static void ConstructString(asString *thisPointer)
{
// Construct the string in the memory received
new(thisPointer) asString();
}

static void ConstructStringByString(const String& text, asString *thisPointer)
{
// Construct the string in the memory received
new(thisPointer) asString(text);
}





// Register the type
r = engine->RegisterObjectType("String", sizeof(asString), asOBJ_CLASS_CDA); jassert( r >= 0 );

// Register the object operator overloads
// Note: We don't have to register the destructor, since the object uses reference counting
r = engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(const String& in)", asFUNCTIONPR(ConstructStringByString, (const String&, asString *), void), asCALL_CDECL_OBJLAST);
r = engine->RegisterObjectBehaviour("String", asBEHAVE_ADDREF, "void f()", asMETHOD(asString,addRef), asCALL_THISCALL); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("String", asBEHAVE_RELEASE, "void f()", asMETHOD(asString,release), asCALL_THISCALL); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("String", asBEHAVE_ASSIGNMENT, "String &f(const String &in)", asMETHODPR(asString, operator =, (const asString&), asString&), asCALL_THISCALL); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("String", asBEHAVE_ADD_ASSIGN, "String &f(const String &in)", asMETHODPR(asString, operator+=, (const asString&), asString&), asCALL_THISCALL); jassert( r >= 0 );

...


last, the regular expression is just returning a new String object (not a pointer) that returns from (c++) String.substr() call...

[Edited by - kunitoki on March 20, 2006 4:59:32 PM]
Looks like you've registered the asString correctly.

Quote:
last, the regular expression is just returning a new String object (not a pointer) that returns from (c++) String.substr() call...


This however sounds a bit suspicious to me. Unless you wrote this incorrectly, this is probably your problem.

You might be getting problems if you're actually returning the C++ String, instead of asString, because the reference counter wouldn't be properly initialized.

You probably have to write the getStringOffset() something like this:

asString *RegularExpression::getStringOffset(int i, int *start, int *end){  // This is where the substring goes  String substr;  // do your normal stuff  // Return a new asString by address (with 1 reference already counted)  return new asString(substr);}


Register it like this:

e->RegisterObjectMethod("RegExp", "String@ getStringOffset(int, int& out, int& out)", asMETHODPR(RegularExpression,getStringOffset), asCALL_THISCALL);


Let me know if that helps.

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

oh, for sure i've mistaken a bit what has to be returned, i wrote in c++ to return a String, but the String returned from the registered function for the script wasn't intended to be c++ String but should have been asString, or script registered "String". i've a String class, wrapped as asString to make it register in script like my original String... i can drive mad out of this ;)
anyway, thanx andreas , you cleared me like like a iron soap... now i follow the right way... ;D
oh, for sure i've mistaken a bit what has to be returned, i wrote in c++ to return a String, but the String returned from the registered function for the script wasn't intended to be c++ String but should have been asString, or script registered "String". i've a String class, wrapped as asString to make it register in script like my original String... i can drive mad out of this ;)
anyway, thanx andreas , you cleared me like like a iron soap... now i follow the right way... ;D
what is strange indeed is the way i registered the RegularExpression to the script. i have my class, then used that for building the object type.

c++ ------------------------------------------

RegularExpression();
RegularExpression( const String& pattern );
~RegularExpression();

int match( const String& subject );
int match( const String& pattern, const String& subject );
String getStringOffsetSize( int num, int& start, int& end );

registration ---------------------------------

r = engine->RegisterObjectType("RegExp", sizeof(RegularExpression), asOBJ_CLASS_CD); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("RegExp", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructRegex), asCALL_CDECL_OBJLAST); jassert( r >= 0 );
r = engine->RegisterObjectBehaviour("RegExp", asBEHAVE_CONSTRUCT, "void f(const String& in)", asFUNCTION(ConstructRegexByString), asCALL_CDECL_OBJLAST);
r = engine->RegisterObjectBehaviour("RegExp", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructRegex), asCALL_CDECL_OBJLAST); jassert( r >= 0 );
r = engine->RegisterObjectMethod("RegExp", "int match(const String& in)", asMETHODPR(RegularExpression,match,(const String&),int), asCALL_THISCALL); jassert( r >= 0 );
r = engine->RegisterObjectMethod("RegExp", "int match(const String& in,const String& in)", asMETHODPR(RegularExpression,match,(const String&,const String&),int), asCALL_THISCALL); jassert( r >= 0 );
r = engine->RegisterObjectMethod("RegExp", "String getStringOffsetSize(int, int& out, int& out)", asMETHODPR(RegularExpression,getStringOffsetSize), asCALL_THISCALL); jassert( r >= 0 );
}


what is interesting is that i registered the member functions passing c++ String not really asString as parameters. and the match functions was working ! maybe because i've declared as member of asString my c++ String like the first and only member variable ?
Quote:
maybe because i've declared as member of asString my c++ String like the first and only member variable ?


It is exactly because of that. I designed asCScriptString with this in mind, i.e. that it should be possible to pass it directly in function parameters expecting a std::string by reference. Since you based your asString on my asCScriptString, you got the same benefits. :)

I'm glad the problem was solved. Let me know if there is anything else I can help with.

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

yeah, maybe you can clarify me on some things. i started using angelcode because of its simplicity, you don't have to read a llot of documentation or references, nor rewrite all of you code twice to embed and publish objects and classes to the script engine, this is clever. anyway, apart from some standalone object that have clearly no interface with others ones (i.e. using/returning basic type like integers, floats..) that i can declare in script directly without any wrapper class, i found that if i have a class A that interacts with class B and wants to publish to the engine as scriptA and scriptB, i noticed that i have to write 2 wrapper classes, and cannot use the main ones, because in the function sign there is the expectation to have class A not scriptA (and for B is the same)... tho is easy indeed to write those wrapper classes, but hey when u got say a hundred functions to publish isn't a fast approach. i have thought about deriving the classes, say i use asString : public String, and then register the methods as usually, but without making the wrapper functions that does nothing more than a simple (asString.innerString)->cppMethod(asString.innerString). you have some advice for me on this side ? i would like to know what you think is the best, easy and fast to write approach for registering mutually interacting classes in the engine.

regards, lucio
Obviously the best way is to have all the classes be directly registerable with AngelScript, e.g. any classes that use reference counting. Even classes that don't use reference counting can usually be registered directly, but without the support for object handles.

For example your String class. You could have registered it directly without using the asString wrapper, but because it would have been without support for object handles (since you have not addref and release behaviours) they it would have been used by AngelScript is a bit restricted.

Anyway, the way you wrote the asString class allow you to register many of the original String methods directly, without the need to write wrapper methods. If you take a close look at the way asCScriptString is registered in RegisterScriptString(), you can actually see that a lot of the functions are registered with the function pointer taken directly from the std::string. For example the comparison operators, and the length() method.

In other situations you can also use AngelScript's autohandle (@+) to allow AngelScript to pass an object handle to an application function that expects a normal pointer, i.e. doesn't update the reference counter. Using the autohandle, AngelScript will automatically maintain the reference counter as needed, without the application function having to do anything.

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