🎉 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!

Bug: CScriptArray last item is null for array<Object@> initialized by list initalizer syntax

Started by
6 comments, last by WitchLord 3 years, 12 months ago

I'm using AngelScript 2.34.0 and using the included CScriptArray add on class to register the `array<T>` script type with native calling convention on macOS.

I have a class field declared as `array<Object@> @objects = { @Object(…), @Object(…), … }` and initialized with all valid handles, but when querying the array the last element of the array is always a `null` handle.

Advertisement

Does your initialization list end with , ?

array<Object@> @objects = { @Object(…), …, @Object(…), };

What value are you using for the engine property asEP_DISALLOW_EMPTY_LIST_ELEMENT? If you haven't changed it, the default behaviour is to insert an uninitialized value when the initialization list has empty values like this.

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

No the list does not end with a trailing comma, nor does it contain any empty items. I've tested with and without the trailing comma and get the same results with the last element nulled out.

As for engine properties, I have no configuration that modifies the defaults so all properties in my solution are the default values. Based on what I see in the angelscript code the default for asEP_DISALLOW_EMPTY_LIST_ELEMENT is false.

I just work around the problem and put a literal null at the end of the list and skip it.

I'm afraid I can't reproduce the problem. I tried the following:

	{
		engine = asCreateScriptEngine();
		engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &amp;bout, asCALL_THISCALL);
		bout.buffer = "";

		engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC);
		RegisterScriptArray(engine, true);

		asIScriptModule* mod = engine->GetModule("test", asGM_ALWAYS_CREATE);
		mod->AddScriptSection("test",
			"class Object {} \n"
			"void main() { \n"
			"  array<Object@> @objects = { @Object(), @Object(), @Object() }; \n"
			"  assert( objects.length() == 3 ); \n"
			"  assert( objects[0] !is null ); \n"
			"  assert( objects[1] !is null ); \n"
			"  assert( objects[2] !is null ); \n"
			"} \n");
		r = mod->Build();
		if (r < 0)
			TEST_FAILED;

		r = ExecuteString(engine, "main()", mod);
		if (r != asEXECUTION_FINISHED)
			TEST_FAILED;

		if (bout.buffer != "")
		{
			PRINTF("%s", bout.buffer.c_str());
			TEST_FAILED;
		}

		engine->ShutDownAndRelease();
	}

Can you identify what else is needed to reproduce the problem you identified?

Does it only happen on macOS?

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 reduced it to a reproducible snippet of code:

funcdef void Callback();

class Object {
  Callback @cb = null;

  Object(Callback @cb = null) {
    @this.cb = cb;
  }
};

class List {
  array<Object@> @objects = {
    @Object(),
    @Object(),
    @Object()
  };
};

void init() {
  message("init()");

  auto @list = List();

  auto len = list.objects.length();
  for (uint i = 0; i < len; i++) {
    auto @s = list.objects[i];
    if (s is null) {
      message("[" + fmtInt(i) + "] = NULL");
      continue;
    }
    message("[" + fmtInt(i) + "] = not null");
  }
}

(I have the host define `void message(const string &in)` and `string fmtInt(int)`)

The issue stems from having the last constructor parameter as optional and set to a default value of `null` combined with eliding the last parameter in the constructor invocations in the initializer list.

This case causes the last instance to be null (aka incorrect unexpected buggy behavior):

  array<Object@> @objects = {
    @Object(null),
    @Object(),
    @Object()
  };

However, this case causes the last instance to NOT be null (aka correct expected behavior):

  array<Object@> @objects = {
    @Object(null),
    @Object(null),
    @Object()
  };

This case also causes the last instance to NOT be null (aka correct expected behavior):

  array<Object@> @objects = {
    @Object(null),
    @Object(null),
    @Object(null)
  };

While reducing the test case, my Object constructor had 3 parameters before the optional 4th parameter and the buggy behavior was still present. Only the last constructor parameter being declared optional affects the outcome.

Thanks for the additional information. I'll investigate this.

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 fixed this in revision 2653.

Thanks,

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