pmed / v8pp

Bind C++ functions and classes into V8 JavaScript engine
http://pmed.github.io/v8pp/
Other
898 stars 120 forks source link

Question: Array of structs #31

Closed atvise closed 7 years ago

atvise commented 7 years ago

Hi,

first of all: great library !

Just want to ask I've anyone know if it's possible to introduce an array of structs to v8 like:

struct MyStruct {
      MyStruct(uint32_t id = 0, const std::string& name = "")
        : id(id)
        , name(name)
      {}
      uint32_t id;
      std::string name;
};

namespace v8pp {
template<>
struct convert<MyStruct>
{
  using from_type = MyStruct;
  using to_type = v8::Handle<v8::Object>;
  static bool is_valid(v8::Isolate*, v8::Handle<v8::Value> value) {
    return !value.IsEmpty() && value->IsObject();
  }
  static to_type to_v8(v8::Isolate* isolate, MyStruct const& p)  {
    v8::EscapableHandleScope scope(isolate);
    v8::Local<v8::Object> obj = v8::Object::New(isolate);
    set_option(isolate, obj, "id", p.id);
    set_option(isolate, obj, "name", p.name);
    return scope.Escape(obj);
  }
  static from_type from_v8(v8::Isolate* isolate, v8::Handle<v8::Value> value)  {
    if (!is_valid(isolate, value))
      throw std::runtime_error("excpected object");
    v8::HandleScope scope(isolate);
    v8::Local<v8::Object> obj = value.As<v8::Object>();
    MyStruct result;
    get_option(isolate, obj, "id", result.id);
    get_option(isolate, obj, "name", result.name);
    return result;
  }
};
}

std::vector<MyStruct> myStructVec;
myStructVec.emplace_back(1,"MaxMuster");
v8pp::context context;
auto isolate = context.isolate();
v8::HandleScope scope(isolate);
v8pp::module input(isolate);
input.set("array", myStructVec);
auto currentGlobContext = isolate->GetCurrentContext()->Global();
auto inputModul = v8::String::NewFromUtf8(isolate, "input");
currentGlobContext->Set(inputModul, input.new_instance());

Furthermore it should be possible to access and change the data within every MyStruct entry of the array in Javascript and access the changed data in C++ after the script finished running ?

I've tried it but can't seem to get it done right because the values aren't changed after executing:

input.array[0].id = 9;
input.array[0].name = "TestName";

Thank you very much for your time and help !

Regards, atvise

pmed commented 7 years ago

Hi @atvise, thanks!

Such a variable binding uses v8::ObjectTemplate::SetAccessor() function to setup getter and setter function callbacks for the variable named array in input object template.

As I understand V8 internals, in expressions like input.array[0].id = 9 V8 tries to access input.array. This leads to the array getter callback invocation, and then V8 gets element of the array which was returned from the getter, and changes it.

But there is no expression in example above, which should look like input.array = [...], so there is no setter callback invocation, and no data update in myStructVec variable on C++ side.

It's similar to modification of a temporary result returned from C++ function:

std::vector<MyStruct> getter()
{
    return myStructVec; // returns a copy of myStructVec
}

getter()[0].id = 9; // no affect on myStructVec

And I see only a workaround like this:

// get data on C++ side,
// getter will convert `myStructVec` into an array of objects
// using `v8pp::convert<MyStruct>::to_v8()` for each vector item
var arr = input.array;

// modify data on JavaScript side
arr[0].id = 9;
arr[1].name = "TestName";

// Set data back to C++ side,
// setter will convert JS array of objects into `std::vector<MyStruct>` 
// using `v8pp::convert<MyStruct>::from_v8` for each array element,
// and then will assign the result vector to myStructVec
input.array = arr
atvise commented 7 years ago

It works like a charm (although with large structs and lots of array elements a small change will also require all data to be copied - performance issue)

But nevertheless you helped me alot! Thank you very much !

pmed commented 7 years ago

Yes, there may be a performance issue for arrays with many elements.

V8 allows to set callbacks with ObjectTemplate::SetIndexedPropertyHandler() to mimics array functionality in wrapped objects. But I've never used this in my projects, so v8pp has no support for this.