Sign in to follow this  
kunitoki

wacky behaviour when using out references (2.6.0 WIP 3)

Recommended Posts

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 ?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 ?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
i see that. is easy if i use a lot of functions that use const String& in passed as parameters, but if i have functions that returns object on the stack, without returning references nor pointers, i have to manually rewrite it. as the RegularExpression function that were running me on troubles, making it returns a newly created asString object instead of just returning the object on the stack. for example i registered functions to work with URLs, that have a function like

class URL {
public:
// ...
const String getContentAsString();
};

or just my String

String substring( int start, int end );

so i should manage this type of functions to be returning a asString* instead, so i have to write wrapper classes for this type of functions, and that's the 80% of the total number of them, cause i don't work too much with pointers in such cases (safer). there is any workaround ?

Share this post


Link to post
Share on other sites
Unfortunately there are no other solutions to this than to write wrapper functions. At least no solution that I can think of at the moment.

AngelScript is expecting you to return an asString object, therefore the application registered function must return an asString object. If it returns a String object instead you're going to run into problems as the two objects are not 100% compatible.

Share this post


Link to post
Share on other sites
If you register the String class as-is, without the wrapper class, then you can also register application functions that return String objects by value.

But, because the String class doesn't have support for object handles, you'll find that AngelScript does a lot of copying of the string objects, which may or may not make a difference to you.

Share this post


Link to post
Share on other sites

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

Sign in to follow this