Preferred way to query C++ from script

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Preferred way to query C++ from script

      Hi,

      I'm working on my own engine at the moment that has a pretty similar structure to GCC and am currently fleshing out my scripting interface. My original design was (as mentioned in GCC3/4) to only use events to transfer information from my scripting languages to C++ and back.

      One of the things I've implemented for my engine is a remote scripting/logging terminal. The engine listens to a socket and executes any incomming script commands that are sent from an external program (which can be on the same machine or over a network). This is incredidbly useful for inspecting the current state of the game and making modifications on the fly (it's also hooked up to my editor).

      However, I frequently find that I want to query the underlying C++ code for information (eg. a list of all entities in the scene, or getting the entityId for an entity with the given name). Doing this the "correct" way would involve and writing a script -> C++ event to send the request for information, the C++ event handler to recieve the request and form a reply, then a C++ to Script event to respond with my information and an event hander in script code to recieve that event and then continue with whatever it was I was doing. Creating new custom events isn't too bad, because I've got some useful factories to generate generic events without having to write the whole class each time. My main frustration is having to break my program flow.

      If I break the rules and directly exposed my C++ EntityManager functions to script I could do this:
      Yes, I know it's python, but my engine has a modular scripting system, so I can switch out scripting languages with relative ease. Learning lua is one of my goals for this project.

      Python Source Code

      1. def getEntityComponents(entityName):
      2. # Get the entity ID from C++ EntityManager class
      3. entityId = EntityManager.getEntityIdFromName(entityName)
      4. if entityId is None:
      5. return
      6. # Print the names of the components attached to this entity
      7. componentNames = EntityManager.getEntityComponents(entityId)
      8. for componentName in componentNames:
      9. print componentName



      However if I went along the events approach it would end up being something more like this:

      Python Source Code

      1. def init():
      2. EventManager.setupEventListener('response_getEntityId', onGetEntityId)
      3. EventManager.setupEventListener('response_getEntityComponents', onGetEntityComponents)
      4. def getEntityComponents(entityName):
      5. # Queue a request for the entity ID from the name
      6. EventManager.QueueEvent('getEntityId', entityName)
      7. def onGetEntityId(entityId=None):
      8. if entityId is None:
      9. return
      10. # Print the names of the components attached to this entity
      11. EventManager.QueueEvent('getEntityComponents', entityId)
      12. def onGetEntityComponents(componentNames=[]):
      13. for componentName in componentNames:
      14. print componentName
      Display All


      Obviously I could optimize this by adding a "getEntityComponentsFromEntityName" event, but it still requires me to wait for a response event before processing the result. It also doesn't make querying the scene for information via the interactive terminal very easy.



      For the moment I have a few exposed functions in a few modules to allow direct calls which return data immediately. My plan is for them to just be for debugging and disable them at a later date, but I'm afraid they're going to prove too tempting to ignore in production code. So I'm just wondering what other people prefer to use to query the C++ layer for information from script?
    • Do both.

      In my own engine, I have script events for sending information in either direction across the boundary. I also have C++ functions and a couple of C++ classes directly exposed to the script. For example, my random number generator is directly exposed as two or three functions rather than a wonky paired event thing. It's faster, easier to debug, easier to understand, and trivial to implement. Why wouldn't I do this?

      Events buy you a couple of things. First, they decouple two systems. If I'm in C++ and need to make a general notification to the script, I can do so without having to guarantee that some script function exists. This works in reverse too; I can send an even from the script and not care where or how the event is processed by the engine. The two can change independently.

      Second, there's a performance gain sometimes. I did a bunch of performance tests and crossing from Lua (or Python) into C++ is really fast, but the opposite is true for crossing from C++ to script. It's REALLY slow to call a script function from C++. Like, painfully slow. So slow that this:

      Source Code

      1. for (int i = 0; i < 1000; ++i)
      2. {
      3. ScriptFunction(); // pretend this is a function that exists on the script
      4. }


      Is WAAYYYYY slower than this:

      Source Code

      1. // C++
      2. DoScriptFunction();
      3. -- Lua
      4. function DoScriptFunction()
      5. for i = 0, 100 do
      6. ScriptFunction()
      7. end
      8. end


      Yup, just the act of calling the function from C++ adds tons of marshaling overhead.

      So, back to performance. One thing you can do is batch all the script events for a frame together and make a single function call into the script, which in turn will process the batch.

      So the unsatisfactory answer is: do what makes sense. I tend to default to events for most things to keep systems nice and decoupled, but I'm also not afraid to use direct function calls. I tend to use direct function calls when it's something really low-level or basic (random number generation, manipulating an entity, etc.) and events for higher level things (set day/time, a piece on the chess board was moved, etc.)

      Hope that helps.

      -Rez
    • Thanks Rez. That is indeed useful information, and probably the route I would have eventually taken if left to my own devices. Having initally decided to use an event & messaging system to decouple as much of my internal code as possible, reading the same statement in GCC just reenforced that concept in my mind, so breaking away from that approach during actual usage felt wrong.