Skip to content

C++ embedding questions #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
IngwiePhoenix opened this issue Jul 11, 2014 · 14 comments
Open

C++ embedding questions #54

IngwiePhoenix opened this issue Jul 11, 2014 · 14 comments

Comments

@IngwiePhoenix
Copy link
Contributor

Hey.

so, I just had to realize that the scripting language that I originalyl wanted to use for my project, has quite some issues. So I need to switch to ObjectScript now. And this brings questions:

  1. How thread-safe is ObjectScript?
  2. Can I compile/run multiple script snippets? Currently, the order would be something like: string, string, string, file, string, string. in my previous used language, ph7, the VM would reset upon each new loaded snippet.
  3. What's the API to insert variables and the like into the scripting eingine?
  4. Can I define getter/setter functions for some native variables?

My project is here: http://github.com/IngwiePhoenix/IceTea

Kind regards, Ingwie

@unitpoint
Copy link
Owner

  1. ObjetcScript is not thread-safe.
  2. You could create a lot of ObjectScript instances. They will work properly.
  3. Please try to investigate datetime extension (ext-datetime). I'll write documentation later.
  4. DateTime class has a lof of getter/setter methods (ext-datetime), for example __get@year, __set@year

@IngwiePhoenix
Copy link
Contributor Author

1: Ok. Gonna use mutexes and guards then.
2: I was rather reffering to a single instance, but calling the function to run OS code off a string/file multiple times. i.e.:

os->runString(...);
os->runString(...);
os->runString(...);
os->runFile(...);
os->runString(...);

Would the VM be overridden and cleared, or would it all jsut behave like one single script? I am asking, because I may define a (scripted) function within the first call, and need that within the second.

3, 4: sure, will do! :) Thanks for that hint.

@unitpoint
Copy link
Owner

You could referring to a single instance, it's ok. The VM will not be cleared, you may define function inside of one file and use it in the second.

@IngwiePhoenix
Copy link
Contributor Author

Awesome! ^^

I began re-writing my implementation to now use ObjectScript instead of the other scripting language, and came across OS::Vextor, OS::Value and OS::GCValue. This reminded me, that I forgot to ask, how I can create an array, object or table to return to ObjectScript. And so far, i noticed that the only documentation about the C++ API is here: https://github.com/unitpoint/objectscript/wiki/Programming-in-ObjectScript#function-in-c

Let's asume I wanted to create a function called "task" with one object-type parameter. If I was calling it like this in OS:

task {
    display = "Special special task",
    cmd = "./binary --args"
};

How would I then recieve the properties of that object in my C++ function?

Secondly, when I use os-binder's ObjectScript::def(), how can I make it pass ObjectScript's object/array/table values? In the example, you only used primitives (float).

@IngwiePhoenix
Copy link
Contributor Author

I just had some more experiments...and it turns out, that I wouldnt get errors when I am trying to call an undefined function. My code for the testing:

#include "objectscript.h"
#include "os-binder.h"

using namespace ObjectScript;

float test(float a, float b){ return a + b; }

int main()
{
    OS * os = OS::create();

    os->setSetting(OS_SETTING_CREATE_TEXT_OPCODES, false);
    os->setSetting(OS_SETTING_CREATE_COMPILED_FILE, false);
    os->setSetting(OS_SETTING_SOURCECODE_MUST_EXIST, true);
    os->setSetting(OS_SETTING_CREATE_DEBUG_INFO, true);

    os->setGlobal(def("test", test));   // use binder

    // Testing multi-call
    os->evalFakeFile("fake.os","bla()");
    os->require("file1.os");
    os->require("file2.os");

    os->release();
    return 0;
}

I was expecting, that I get some sort of error message or a like with the first call. But I dont. O.o How do I pick up errors like that, or have I overseen some setting?

@unitpoint
Copy link
Owner

You could define global getter function to detect that error, OS script:

function __get(name){
    throw "unknown class or global property \"${name}\""
}

@unitpoint
Copy link
Owner

Let's asume I wanted to create a function called "task" with one object-type parameter. If I was calling it like this in OS:

task {
    display = "Special special task",
    cmd = "./binary --args"
};

You could iterate OS object or array using nextIteratorStep method, for example:

int my_func(OS * os, int params, int, int, void * user_param)
{
    if(os->isObject(-params+0)){
        os->pushStackValue(-params+0);
        while(os->nextIteratorStep()){
            OS::String key = os->toString(-2);
            OS::String value = os->toString(-1);
            ...
            os->pop(2);
        }
        os->pop();
        return 0;
    }
    ...
}

@IngwiePhoenix
Copy link
Contributor Author

Cool! That would help me to turn it into a map<>. Pretty neat.
I also made __get in C++, was easier than I thought and now I know how to set exceptions.

But I now am wondering about something I just came across. I am writing a wrapper about (config4cpp)[http://config4star.org] to catch special config parts. But i have two different configs. One stays the same all the time, so its initialized directly. But the second is one that keeps changing. So I was thinking about dynamically constructing the neccessary object. I have figured out how to register primary values - numbers, strings, etc. - but how do I do functions? Are they the same typedef than the one used for regular, global, functions?

Also later, I will post a link to my embedding work, to showcase what I am doing.

@IngwiePhoenix
Copy link
Contributor Author

https://github.com/IngwiePhoenix/IceTea/blob/master/src/os-icetea.cpp Voila.

I am trying to come up with a good solution for the config part at the bottom. I always have up to three pointers. One is the global and always active one. The other two are dynamic. So I am thinking of either using getter/setter, a prototype, or construct a new object with the above get and set method. What do you think would be the easiest to achieve?

@unitpoint
Copy link
Owner

I'd recommend to use config in JSON format if possible (see json.encode, json.decode). Or in ObjectScript format, for example OS config file my_config.os:

return {
    class = "DbConnection",
    type = "odbc",
    tablePrefix = "med_",
    params = {
        DRIVER = "{SQL Server}",
        SERVER = "test",
        DATABASE = "MIS",
        USER = "username",
        PASSWORD = "passwd",
    },
}

and load the config file:

var config = require("my_config.os")

@IngwiePhoenix
Copy link
Contributor Author

I see. o.o

Okay, one more thing. I know that one can construct a new module, or extend one, in C++. But how do I just create a global object? Like this?

os->newObject();
os->pushCFunction(myfunc, (void*)something);
os->setProperty("func");
os->setGlobal("obj");

@unitpoint
Copy link
Owner

You can do it like this:

os->newObject();
os->pushStackValue(-1);
os->pushCFunction(myfunc, (void*)something);
os->setProperty("func");
os->setGlobal("obj");

or

os->newObject();
os->pushCFunction(myfunc, (void*)something);
os->setProperty(-2, "func");
os->setGlobal("obj");

@IngwiePhoenix
Copy link
Contributor Author

Huh? I mean, I am getting the deal with a stack-based VM, but why do I need to set negative offsets (-1, -2) for these? Is it, so that the property is being set to the new object?

0 Object
1 CFunction

So that when then property is being set, it points to 0?
Am 25.07.2014 um 18:34 schrieb Evgeniy Golovin [email protected]:

You can do it like this:

os->newObject();
os->pushStackValue(-1);
os->pushCFunction(myfunc, (void*)something);
os->setProperty("func");
os->setGlobal("obj");
or

os->newObject();
os->pushCFunction(myfunc, (void*)something);
os->setProperty(-2, "func");
os->setGlobal("obj");

Reply to this email directly or view it on GitHub.

@unitpoint
Copy link
Owner

Negative offset is used for relative offset, so -1 is top stack value, -2 is value before the top value and so on. Positive offset is used for absolute offset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants