In a C++ NodeJS project I'm currently working on, I want to have an object which contains a nested object, like
console.log(object.myObject); // { [keys: string]: any }
Without binding objects to properties my addon is working very well. I create the Instance with
auto ctor = Nan::New<v8::FunctionTemplate>(New);
auto ctorInst = ctor->InstanceTemplate();
And bind the object to the class instance like
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
ctorInst->Set(Nan::New("myObject").ToLocalChecked(), obj);
which prints the following when executing the JS code (create an instance of the C++ extension).
# Fatal error in , line 0
# Check failed: !value_obj->IsJSReceiver() || value_obj->IsTemplateInfo().
On the other hand binding primitives (number, string, etc.) works flawlessly.
ctorInst->Set(Nan::New("myObject").ToLocalChecked(), Nan::New<v8::Number>(42));
Do you have got any suggestions? Thanks and cheers!
All templates (Including InstanceTemplate's) can only have other templates, or primitives, in them. As you see, IsTemplateInfo was false, when it should have been true. A similar question was asked and answered here. Simply make an ObjectTemplate and then populate it like you were doing with the FunctionTemplate.
Only when the Template is instantiated (with GetFunction), can you populate it with real objects.
In addition to Nicholas' answer I'd like to add the following.
Instance
Simply replace the v8::Object with v8::ObjectTemplate. Set properties to the object template and bind the template to the addon instance.
v8::Local<v8::ObjectTemplate> objTemplate = Nan::New<v8::ObjectTemplate>();
objTemplate->Set(Nan::New("prop").ToLocalChecked(), Nan::New<v8::Number>(42));
ctorInst->Set(Nan::New("myObject").ToLocalChecked(), objTemplate);
Now you are able to use
console.log(addonInstance.myObject); // { prop: 42 }
Prototype
If you want to bind functions to the object, create an object template like above. Then create a function template from an existing cpp function, and bind it to an object property of the object template. Then create a template on the addon instances prototype.
v8::Local<v8::ObjectTemplate> objTemplate = Nan::New<v8::ObjectTemplate>();
objTemplate->Set(Nan::New("prop").ToLocalChecked(), Nan::New<v8::Number>(42));
auto fn = Nan::New<v8::FunctionTemplate>(setup);
objTemplate->Set(Nan::New("setup").ToLocalChecked(), fn);
Nan::SetPrototypeTemplate(ctor, "myObj", objTemplate);
and the setup function is simply
NAN_METHOD(YourClass::setup) {
info.GetReturnValue().Set(Nan::New<v8::String>("Hello world").ToLocalChecked());
}
The console output would be
console.log(addonInstance.myObject) // { prop: 42, setup: [Function: setup] }
Related
I'm confused by the difference between V8 ObjectTemplate's Set and SetAccessor methods. My question has a simple context and 4 concrete sub-questions.
Context
Let's say I have code snippet that wants to provide a global object global to the targeting JS context. global has a property x, whose value takes int value. global also has a property log, which is a function. All the snippets are taken from V8's source, process.cc and Embedder's Guide to be precise.
HandleScope handle_scope(GetIsolate());
// Create a template for the global object where we set the
// built-in global functions.
Local<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
global->Set(String::NewFromUtf8(GetIsolate(), "log",
NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(GetIsolate(), LogCallback));
So this code snippet provides function log to the global. Then from the Embedder's Guide to accessors, it says
An accessor is a C++ callback that calculates and returns a value when an object property is accessed by a JavaScript script. Accessors are configured through an object template, using the SetAccessor method.
The code snippet follows:
void XGetter(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
info.GetReturnValue().Set(x);
}
void XSetter(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<Value>& info) {
x = value->Int32Value();
}
// YGetter/YSetter are so similar they are omitted for brevity
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "x"), XGetter, XSetter);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "y"), YGetter, YSetter);
Persistent<Context> context = Context::New(isolate, NULL, global_templ);
As I understand this code snippet, it's providing some integer value x to the global as the description goes.
Now,from the source of V8, I see ObjectTemplate doesn't have a Set method, instead, it's inherited from parent class Template. From Template's source code, it says:
/**
* Adds a property to each instance created by this template.
*
* The property must be defined either as a primitive value, or a template.
*/
void Set(Local<Name> name, Local<Data> value,
propertyAttribute attributes = None);
Questions
Template's Set method says it can set a primitive value to the instance of the template, then can I use Set to set x in the second code snippet instead of using SetAccessor?
If the answer to question 1 is true, then what's the difference for setting x between using SetMethod and Set? Is the difference being that any modification in JS to the property set by Set will not be reflected in C++?
If the answer to question 1 is false, then why can't I use Set on X?
From the description of accessors, it says it computes and return value. So does it mean we don't use SetAccessor to return functions? I'm confused because I mainly write JS and Haskell. Both languages spoils me to take functions as values.
Now I know it should be easy to verify all my assumptions by actually building the samples, but I have difficulties compiling the V8 source, hence I'm asking for any help.
Thank you in advanced for any effort!
1. Yes.
2. Set is the C++ equivalent (modulo property attributes) of:
Object.defineProperty(global, "x", {value: 3})
SetAccessor is the C++ equivalent of:
Object.defineProperty(global, "x", {get: function XGetter() { return ...; },
set: function XSetter(val) { ... }});
As you suggest, a consequence is that in case of Set, the C++ side has no way of knowing whether the value was changed from the JavaScript side.
3. n/a
4. The getter can return any value you want; in particular the value can be a function.
I'm using Luabind to bind my Lua scripts to my C++ engine. (it uses lua 5.1.4)
I added a new lua script called "controller.lua" which my entities script, called "cat.lua", will reference and use. One the c++ calls the method "Update" it's all in the hands of Lua.
But once I try to pass my binded c++ methods to the new script file, it feels like all the bindings from that c++ object disapear. I get the following error:
Expression: scripts/controller.lua:5(method MoveUp)
scripts/controller.lua:5: attempt to call method 'GetComponent' (a nil value)
Here is some C++ snippets
// Definitions
module(luaState)
[
class_<Entity>("Entity")
.def("GetComponent", &Entity::GetComponent)
class_<Component>("Component")
.enum_("eComponentTypes")
[
value("Steering", kComponentType_Steering)
],
class_<SteeringComponent>("SteeringComponent")
];
// The script components update
void ScriptComponent::Update() {
const Entity* owner = this.GetOwner();
mLuaDataTable["Update"](owner); // Executes the Update function on the script Cat.lua
}
The entities code being called by c++ (When it executes it returns the Cat table to c++.)
-- Cat.lua
local controller = loadfile("scripts/controller.lua")
local Cat = {}
function Cat.Update(entity)
steeringComponent = entity:GetComponent(Component.Steering) -- Works fine
controller:MoveUp(entity)
end
return Cat
and the Controller
--controller.lua
local up = vec2(0.0, 1.0)
local Controller = {}
function Controller.MoveUp(entity)
steeringComponent = entity:GetComponent(Component.Steering) -- Fails
end
return Controller
Bonus points:
When I make a change to the controller that doesn't work (like if I just threw an s character anywhere), the controller loads up nil, no warnings. Is there some way to make it throw warnings?
Is there a better way I should be doing to "link" to other lua files, like the way im working with Controller?
Thanks to ToxicFrog on Freenode chat for helping me figure this one out.
Basically: I was calling controller MoveUp like so:
controller:MoveUp(entity)
which of course translates into
controller.MoveUp(controller, entity)
and the function was defined as
function Controller.MoveUp(entity)
this "entity" was accepted as the first parameter, controller, while the actual entity is discarded per spec.
http://lua-users.org/wiki/ObjectOrientationTutorial
The following is an API reference for a method in "QmlDocument" class(Blackberry10).
Builder create (const QString &qmlAsset, boolautoLoad )
Creates and returns a builder for constructing a QmlDocument instance
with a parent object and an asset name to load the document from.
Parameters qmlAsset The QML asset name load the document from,
specified relative to the assets root. autoLoad if true the document
is automatic loaded, otherwise it is required to call load function
explicitly. The default is true . Since: BlackBerry 10.0.0
Now what is exactly meant by a "Builder" here. What is its purpose? what is the difference of creating an object from QmlDocument class with "new" keyword, and creating the object with the method defined above?
Builders are usually classes defined locally to the associated class (ie QmlDocument::Builder) that allow chaning of methods with operator . () in a way similar to that done with iostreams and operator << (). What it gets you is a more readable way of createing objects (and potentially their childre) in one statment rather than creating with a new operator and a number of function calls. A better example than QmlDocument might be the Container class:
Container *container1 = Container::create()
.preferredSize(200, 200)
.background(Color::Blue);
This creates a new Container, sets the preferred size and background color. The implementations details are hidden. Somewhat simmilar to an opaque type in C.
Is it possible to get an existing object reference using CEF API?
For example I run a script using ExecuteJavaScript()
function foo()
{
var self = this;
self.value="some value";
}
var fooObj = new foo;
This script creates a new variable fooObj. It is possible to get a reference to this variable later in the C++ code and to modify its value?
You should be able to get it by doing something like the following (untested):
auto context = AppGetBrowser()->GetMainFrame()->GetV8Context();
CefRefPtr<CefV8Value> p = context->GetGlobal()->GetValue(CefString("fooObj"));
You may need to Enter/Exit the context depending on where you're calling it from in C++. Furthermore you may need to actually reference your object explicitly as 'window.fooObj' in which case you'll have to get the value for 'window' and then get 'fooObj' off that.
(edit - accidentally posted too early)
(edit 2 - more)
So I'm working on a little project in which I'm using Python as an embedded scripting engine. So far I've not had much trouble with it using boost.python, but there's something I'd like to do with it if it's possible.
Basically, Python can be used to extend my C++ classes by adding functions and even data values to the class. I'd like to be able to have these persist in the C++ side, so one python function can add data members to a class, and then later the same instance passed to a different function will still have them. The goal here being to write a generic core engine in C++, and let users extend it in Python in any way they need without ever having to touch the C++.
So what I thought would work was that I would store a boost::python::object in the C++ class as a value self, and when calling the python from the C++, I'd send that python object through boost::python::ptr(), so that modifications on the python side would persist back to the C++ class. Unfortunately when I try this, I get the following error:
TypeError: No to_python (by-value) converter found for C++ type: boost::python::api::object
Is there any way of passing an object directly to a python function like that, or any other way I can go about this to achieve my desired result?
Thanks in advance for any help. :)
Got this fantastic solution from the c++sig mailing list.
Implement a std::map<std::string, boost::python::object> in the C++ class, then overload __getattr__() and __setattr__() to read from and write to that std::map. Then just send it to the python with boost::python::ptr() as usual, no need to keep an object around on the C++ side or send one to the python. It works perfectly.
Edit: I also found I had to override the __setattr__() function in a special way as it was breaking things I added with add_property(). Those things worked fine when getting them, since python checks a class's attributes before calling __getattr__(), but there's no such check with __setattr__(). It just calls it directly. So I had to make some changes to turn this into a full solution. Here's the full implementation of the solution:
First create a global variable:
boost::python::object PyMyModule_global;
Create a class as follows (with whatever other information you want to add to it):
class MyClass
{
public:
//Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here.
boost::python::object Py_GetAttr(std::string str)
{
if(dict.find(str) == dict.end())
{
PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str());
throw boost::python::error_already_set();
}
return dict[str];
}
//However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__.
//Which means anything that's been defined as a class attribute won't be modified here - including things set with
//add_property(), def_readwrite(), etc.
void Py_SetAttr(std::string str, boost::python::object val)
{
try
{
//First we check to see if the class has an attribute by this name.
boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str());
//If so, we call the old cached __setattr__ function.
PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val);
}
catch(boost::python::error_already_set &e)
{
//If it threw an exception, that means that there is no such attribute.
//Put it on the persistent dict.
PyErr_Clear();
dict[str] = val;
}
}
private:
std::map<std::string, boost::python::object> dict;
};
Then define the python module as follows, adding whatever other defs and properties you want:
BOOST_PYTHON_MODULE(MyModule)
{
boost::python::class_<MyClass>("MyClass", boost::python::no_init)
.def("__getattr__", &MyClass::Py_GetAttr)
.def("__setattr_new__", &MyClass::Py_SetAttr);
}
Then initialize python:
void PyInit()
{
//Initialize module
PyImport_AppendInittab( "MyModule", &initMyModule );
//Initialize Python
Py_Initialize();
//Grab __main__ and its globals
boost::python::object main = boost::python::import("__main__");
boost::python::object global = main.attr("__dict__");
//Import the module and grab its globals
boost::python::object PyMyModule = boost::python::import("MyModule");
global["MyModule"] = PyMyModule;
PyMyModule_global = PyMyModule.attr("__dict__");
//Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones
PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__");
PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__");
}
Once you've done all of this, you'll be able to persist changes to the instance made in python over to the C++. Anything that's defined in C++ as an attribute will be handled properly, and anything that's not will be appended to dict instead of the class's __dict__.