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

Using objects in AS

Started by
12 comments, last by WitchLord 16 years, 10 months ago
Yep, I'm back already with more questions! (I should probably write up a tutorial of my own after all this. ;) ) Okay, so I'm having a bit of trouble using objects instantiated from classes from the host C++ program. I was able to successfully expose my ScriptingEngine object from before; however, I'm getting some errors/lockups when it comes to creating a new object within AS from a class I exposed. Here's my code: C++ code
   
   r = engine->RegisterObjectType("CondVar", sizeof(CondVar), asOBJ_CLASS); assert (r >= 0);
   r = engine->RegisterObjectMethod("CondVar", "void Set_Name(string)", asMETHOD(CondVar,Set_Name), asCALL_THISCALL); assert (r >= 0);
   r = engine->RegisterObjectMethod("CondVar", "void Set_Value(string)", asMETHOD(CondVar,Set_Name), asCALL_THISCALL); assert (r >= 0);
   r = engine->RegisterObjectMethod("CondVar", "string Get_Name()", asMETHOD(CondVar,Get_Name), asCALL_THISCALL); assert (r >= 0);
   r = engine->RegisterObjectMethod("CondVar", "string Get_Value()", asMETHOD(CondVar,Get_Value), asCALL_THISCALL); assert (r >= 0);

CondVar definition

class CondVar : public Object
{

   public:
      CondVar() {}
      CondVar(string n, string v) { name = n; value = v; }
      ~CondVar() {}
      
      // accessors
      void Set_Name(string n) { name = n; }
      void Set_Value(string n) { value = n; }
      
      string Get_Name() { return name; }
      string Get_Value() { return value; }
      
   private:
      string name,
             value;

};

AS function that's being called (ignore the part about taking floats; it originally did a calculation and printed it)

void PrintResult(float x, float y)
{
   CondVar cv;
   cv.Set_Name("foo");
   cv.Set_Value("bar");
}

As you can see, CondVar is just a simple key, with a string name and a string value. Whenever I type a command to have PrintResult() called, I get the whole "Send error report to Microsoft" deal. I thought at first that it could be that I needed to register the Object class as well, but I tried and to no avail. Plus, now that I better (but not *much* better) understand how AS method registration works, it wouldn't make sense to have to do that. Am I missing something here? Thanks in advance for your help, as always.
Advertisement
There are a couple problems and potential problems with the code you posted.

1) You do not want to register your Set_Name and Set_Value functions to take a string by value, you want them to take an in reference.

For instance, you registered them to AngelScript as:
void Set_Name(string)
void Set_Value(string)

But you should've registered them as:
void Set_Name(const string ∈)
void Set_Value(const string ∈)

This is so AngelScript can pass the data by reference and not by value - which is faster and less bug-prone (due to the internal workings of the native function calling, some platforms, depending on the makeup of the class, will not like pass by value at all). NOTE: I made them const above because it is even better to pass data, that won't be changed, as a const reference.

2) You will then need to change your CondVar::Set_Name and CondVar::Set_Value functions to reflect the changes above. They will now need to take a reference to the string. So these now become:

// accessors
void Set_Name(const std::string &n) { name = n; }
void Set_Value(const std::string &n) { value = n; }


3) You may want to register your constructors and destructor as behaviors for the class, using asIScriptEngine::RegisterObjectBehavior. Use asOBJ_CLASS_CD instead of asOBJ_CLASS then.
midnite is correct.

I just want to reinforce that you must register the constructor for this class. If you don't register the constructor, the class members won't be initialized properly, which means that when you're calling any of the class members that uses the members you'll be accessing bogus data (which is likely what is causing the application crash you're experiencing). If you don't register the destructor for this class, you'll have memory leaks, but it shouldn't cause any application crashes (at least not immediately).

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

Hmm... but this CondVar class is also used extensively throughout my host C++ program... so what happens if some function generates two local strings for name and value, assigns them to a global CondVar, then returns? Wouldn't the originals disappear due to the function's scope, and the references to them in said global CondVar be erroneous?
Still store the CondVar data by value; just change the functions. CondVar will store its own copies of the data.
Oh wait... yeah, okay. That makes perfect sense. Thanks, SiCrane.

I've adapted my program and classes, and everything's working fine now.
One other quick question: should I be doing something with references on string return values? Cause the following fails to build:

C++
string ScriptingEngine::Get_CondVar_Value(const string &name){   return condvars->Get_Var(name).Get_Value();}   r = engine->RegisterObjectType("ScriptingEngine", sizeof(ScriptingEngine), asOBJ_CLASS); assert (r >= 0);      r = engine->RegisterObjectMethod("ScriptingEngine", "string Get_CondVar_Value(const string &in)", asMETHOD(ScriptingEngine,Get_CondVar_Value), asCALL_THISCALL); assert (r >= 0);   r = engine->RegisterGlobalProperty("ScriptingEngine engine", this); assert (r >= 0);


AS
void PrintResult(float x, float y){       string name = "room006_visited";   string value = Get_CondVar_Value(name);   engine.Print("temp name: " + name + " value: " + value + "\n");}
You should not return the string by value. Return it a constant string reference instead.

   r = engine->RegisterObjectMethod("ScriptingEngine", "const string &Get_CondVar_Value(const string ∈)", asMETHOD(ScriptingEngine,Get_CondVar_Value), asCALL_THISCALL); assert (r >= 0);const string &ScriptingEngine::Get_CondVar_Value(const string &name){   return condvars->Get_Var(name).Get_Value();}


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

Hmmm... I've changed it as you suggested, but I'm still having a problem...

The script compiles fine, but will crash the program:
void PrintResult(float x, float y){   string name = "room006_visited";   string value = engine.Get_CondVar_Value(name);}


However, the following will NOT crash the program:
void PrintResult(float x, float y){   string name = "room006_visited";   engine.Get_CondVar_Value(name);}


So something's going wrong when I try to assign the returned string to an AS string.
Nevermind! I figured out the problem on my own. Turns out that CondVar::Get_Value() didn't return values by reference, which meant that ScriptingEngine::Get_CondVar_Value() was returning a reference to a local variable, which is a good enough reason to crash the app.

Thanks for everyone's help on this matter, though!

This topic is closed to new replies.

Advertisement