Search code examples
c++stdmapchaiscript

How can I add a global std::map to chaiscript?


I would like to pass a std::map to chaiscript. However, I'm not sure how to do this. My code is currently as follows:

#include <map>
#include <string>
#include <chaiscript/chaiscript.hpp>

int main(int argc, char* argv[]) {
    chaiscript::ChaiScript chai;
    auto data = std::map<std::string, std::string>{
        { "key1", "val1"},
        { "key2", "val2"},
    };
    chai.add_global(chaiscript::var(&data), "data");
    chai.eval(R"(
        print(data["key1"]);
    )");
}

However, this code crashes with an exception saying, that chaiscript doesn't know what to do with the bracket [] operator. How can I fix this?

I could tell chaiscript what the right function is, but I would prefer it, if the map is compatible with chaiscripts internal Map type!

Update:

I found a bit in the documentation, which explains that the chaiscript map type supports arbitrary input. Looking at the code, this seems to be done by the Boxed_Value type. However, this probably means that it is fundamentally impossible to directly insert std::map into scripts.

I'm now thinking about either writing a custom type, or a conversion function to solve the problem. Keeping you posted...


Solution

  • As far as I can see, out-of-the-box-chaiscript only provides you with the std::map<std::string,chaiscript::Boxed_Value> map type. Therefore, if you want to add your own map to a script, you need to either provide chaiscript with a new type, or convert to the given one. Thus, I see the following solutions:

    Case 1: You only need to get out a map from chaiscript to c++

    This case can be found in the documentation. You need to supply a conversion function, and off you go.

    chai.add(chaiscript::map_conversion<std::map<std::string, std::string>>());
    auto map = chai.boxed_cast<std::map<std::string, std::string>>(chai.eval("data"));
    

    Case 2: You only need to supply a map to chaiscript from c++

    This is basically the same as Case 1, but you have to supply the conversion function yourself.

    auto convert = [](const std::map<std::string, std::string>& std_map) {
        auto chai_map = std::map<std::string, chaiscript::Boxed_Value>{};
        for (auto&& entry : std_map)
            chai_map.emplace(entry.first, chaiscript::var(entry.second));
        return chai_map;
    };
    chai.add(chaiscript::var(convert(data)),"data");
    

    Case 3: You want to share a global value between chaiscript and c++

    This case is rather tricky. You either have to supply chaiscript with a get_map() and send_map() function, which handle the data synchronization:

    chai.eval(R"(
        data = get_map();
        data["key1"] = "val1";
        send_map(data);
    )");
    

    Or you have to add a custom data type, which handles the synchronization in the background.

    My Solution:

    Fortunately for my case, I don't really need a shared state between chaiscript and c++, and therefore can rely on the solution for Case 2.