🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Index operator

Started by
4 comments, last by WitchLord 18 years, 7 months ago
I'm trying to get the [] operator registered in my application, and AngelScript keeps on crashing on Build(). If I use ints/floats at the index parameter, everything is fine. The problem occurs when I try to use strings. Below is a simple test case for this (note that using a by-value string as the index is on purpose, although I have tried this using references/handles with the same effect). // asTest1.cpp

#include <windows.h>
#include <iostream>
#include <string>
#include <cassert>
using namespace std;

#include "angelscript.h"
#pragma comment(lib, "angelscript.lib")

struct : public asIOutputStream
{
	void Write(const char* text)
	{
		cout << text;
	}
}
asOutput;

struct CTestObj
{
	CTestObj& operator[](string s);
};

string asStringFactory(asUINT length, const char *text);
void asStringConstructor(string* pthis);
void asStringDestructor(string* pthis);
string& asStringAssignString(string &cp, string *pthis);
bool GetScript(char* filename, char* &code, unsigned int& size);

void main(void)
{
	int rc;
	char* code;
	char* szTestFile;
	unsigned int size;
	asIScriptEngine* engine;
	
	szTestFile = "asTest1.as";

	if (!GetScript(szTestFile, code, size))
		return;
	
	engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
	engine->SetCommonMessageStream(&asOutput);

	rc = engine->RegisterObjectType("string", sizeof(string), asOBJ_CLASS_CDA); assert(rc >= 0);
	rc = engine->RegisterStringFactory("string", asFUNCTION(asStringFactory), asCALL_CDECL); assert(rc >= 0);
	rc = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(asStringConstructor), asCALL_CDECL_OBJLAST); assert(rc >= 0);
	rc = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(asStringDestructor), asCALL_CDECL_OBJLAST); assert(rc >= 0);
	rc = engine->RegisterObjectBehaviour("string", asBEHAVE_ASSIGNMENT, "string& f(string &in)", asFUNCTION(asStringAssignString), asCALL_CDECL_OBJLAST); assert(rc >= 0);

	rc = engine->RegisterObjectType("CTestObj", sizeof(CTestObj), asOBJ_CLASS); assert(rc >= 0);
	rc = engine->RegisterObjectBehaviour("CTestObj", asBEHAVE_INDEX, "CTestObj& f(string)", asMETHOD(CTestObj, operator[]), asCALL_THISCALL); assert(rc >= 0);

	engine->AddScriptSection(0, szTestFile, code, size);
	engine->Build(0);
	engine->ExecuteString(0, "main()");
	engine->Release();
}

string asStringFactory(asUINT length, const char *text)
{
	cout << __FUNCTION__ << " length=" << length << " text='" << text << "'\n";
	return string(text);
}

void asStringConstructor(string* pthis)
{
	cout << __FUNCTION__ << " pthis=" << (int)pthis << endl;
	new(pthis)string();
}

void asStringDestructor(string* pthis)
{
	cout << __FUNCTION__ << " pthis=" << (int)pthis << endl;
	pthis->~string();
}

string& asStringAssignString(string &cp, string *pthis)
{
	cout << __FUNCTION__ << " cp=" << cp << " pthis=" << (int)pthis << endl;
	return *pthis = cp; 
}

bool GetScript(char* filename, char* &code, unsigned int& size)
{
	DWORD dummy;

	HANDLE hFile = CreateFile(
		filename,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		size = GetFileSize(hFile, 0);
		code = new char[size + 1];

		ReadFile(hFile, code, size, &dummy, NULL);
		CloseHandle(hFile);

		cout << "Script '" << filename << "' is " << size << " bytes\n";
	}
	else
		cout << "Script '" << filename << "' does not exist\n";

	return hFile != INVALID_HANDLE_VALUE;
}

CTestObj& CTestObj::operator[](string s)
{
	cout << __FUNCTION__ << " s='" << s << "'\n";

	return *this;
}

// asTest1.as

void main()
{
	CTestObj obj;

	obj["test"];	
}

Is anyone else successfully using strings as index parameters? -D
Advertisement
It could be a bug in AngelScript. I don't remember testing this situation, I'll do that as soon as I can.

In either case, I recommend that you take the string by reference, instead of by value in the indexing operator. With the current version of AngelScript it doesn't make much difference as AngelScript makes a copy of the string anyway, but in upcoming version the compiler will be more intelligent and only make the copy when absolutely necessary.

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

I have it by-value in my engine at the moment. Reason being is I do a lot of manipulation to the incoming string, and by-value saves me the need of creating a 2nd temporary string to hold the changes. Is there anything behind the scenes occuring in AngelScript that would make this much more inefficient than normal by-value parameters in C++?

-D
Well, C++ will always be much more efficient since it can statically determine what to do at compile time.

AngelScript does most of the work dynamically, i.e. it allocates the memory for the object dynamically on the heap then calls the assignment operator to copy the object. When calling the C++ function the string is copied once more to the stack.

However, if you don't need extremely high performance perhaps it is just better to send the string by value, since it simplifies your implementation.

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

I've been able to reproduce the problem and I'm working on a solution for it.

Does your indexing operator have to return a reference when taking a string as parameter? The problem only occurs because you're returning a reference and have an object types as parameter. If you could return for example an object handle instead, there shouldn't be any problem. Or if that doesn't work, then you could substitute the indexing operator for a method call, until I've completed the fix.

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

Well, this was an easy one to fix. [smile]

Just add the following statement in file as_compiler.cpp at line 5797:

argNodes.PushLast(0);


The code will then look like this:

void asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ctx){	int op = node->tokenType;	if( op == ttInc || op == ttDec )	{		...	}	else if( op == ttDot )	{		...	}	else if( op == ttOpenBracket )	{		...		asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();		if( beh == 0 )		{			...		}		else		{			...			// Did we find a suitable function?			if( ops1.GetLength() == 1 )			{				...				// Add the code for the object again				ctx->bc.AddCode(&objBC);				asCArray<asCTypeInfo> argTypes;				argTypes.PushLast(expr.type);				asCArray<asCScriptNode*> argNodes;				argNodes.PushLast(0);				PerformFunctionCall(descr->id, ctx, false, &argTypes, &argNodes);				// TODO: Ugly code				// The default array returns a reference to the subtype				...			}			else if( ops.GetLength() > 1 )			{				...			}			else			{				...			}		}		...	}}


Let me know if it works for you as well.

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

This topic is closed to new replies.

Advertisement