Is it possible to execute a js function with a cpp object as argument in v8?
if yes then how?
Lets say there is a function in javascript to traverse a Node tree .
function traverse_tree(root) /* root is type of Node */
{
no_of_node++; /* no_of_node is a global variable in js */
for(var i=0;i<root.children.length;i++)
{
traverse_tree(root.children[i]);
}
}
and the Node tree is created in C++;
class Node
{
public:
std::vector<Node*> children;
}
how can I pass the root of the tree structure created in cpp in the function calling argument?
Short Answer
No, it won't be possible for you to access the actual C++ object from javascript.
Webkit bridge works by making methods of a C++ object available in javascript under a particular name.
Simple variables such as strings or integers can be passed as parameters to these function calls.
Passing more complex objects is possible as long as they are HashMaps (key-value pairs).
This means that it's not possible to pass a reference so that the javascript and C++ "share" the object.
Possible Implementations
You could create a method in your C++ application (either as a separate class or as part of Node) and expose that object to javascript. Then any implementation you require (such as searching for a particular item) can be done in C++. In this manner you've created a javascript API to your C++ applications functionality.
If you really need to do the processing in javascript then you'd have to figure out a way to convert your tree into some kind of HashMap (so that it's meaningful as a JSON object) and then return it as a parameter to javascript.
Related
If I am given a pointer to an object and I need to pass one of that object's methods as an argument to another function, is that possible?
A very simplified example would look like this:
void consumeAFunction(Function func) {
auto value = func();
// do some stuff //
}
void main(Object *pointerToObject) {
consumeAFunction(pointerToObject->someMethod)
}
I've tried the following, but I think my understanding of pointers and references is flawed. I'm 3 weeks old in my c++ journey.
Object someObject = pointerToObject and Object someObject = *pointerToObject
The specific context of the question is that I have a pointer to an object created by some other library and I need to use QtConcurrent::run on that object's methods.
Additional context
consumeAFunction is QtConcurrent::run
Function func is a method of an Engine that simply performs some logic. I am handed a pointer to Engine by a third party library.
I cannot avoid using a pointer to Engine, because it is all I am given to work with.
As much of the specific code as I am allowed to show:
// engine is the pointer to someObject:
auto engine = lui::QueryInterop<wise::Engine>(lui::GetLUI());
if (engine) {
connect(&m_modelsLoadedWatcher, &QFutureWatcher<bool>::finished, this, &ConfigDialog::onNNModelsLoaded);
// This is the call to consumeAFunction (qtconcurrent::run)
m_modelsLoadedFuture = QtConcurrent::run(engine->loadPytorchModels);
m_modelsLoadedWatcher.setFuture(m_modelsLoadedFuture);
Because this is a Qt question, I highly recommend you get an understanding of QObject and QMetaObject::invokeMethod().
Because QObject is pre-processing via the moc-compiler, a lot of public interfaces, such as properties, methods are exposed in such a way that the object's properties and methods can be inspected at runtime by another plugin and that it doesn't need to know or have access to the header files. This is why something like QMetaObject::invokeMethod() can work because it has access to the metadata.
Alternatively, if you are using Javascript a lot in QML, you may be interested in passing a Javascript callback function to C++. That function is accessible via QJSValue. QJSValue usually is used to hold simple types such as strings and numbers. When it holds more complex Javascript types such as arrays, objects, or functions you can use quite a few QJSValue methods to unlock their capabilities. In the case of Javascript functions you can verify if it is a Javascript function with QJSValue.isCallable() == true and can you can execute it with QJSValue.call(...).
I am creating a V8 wrapper to my C++ object by using v8::ObjectTemplate. But on the step of generation this template I don't have full type info about C++ object and as result on JS runtime I have JS object with incomplete list of functions and properties.
And now if user try call a function that I not describe in ObjectTemplate, V8 just throw an exception in script.
Is there way to set callback which will invoke before an exception will throw?
This callback should make a deep search of user called function and return execution result it successful found.
//c++
class A{
public:
static int f1(){return 1;}
static int f2(){return 2;}
};
void create_v8_template(){
v8::Local<v8::ObjectTemplate> template_base = v8::ObjectTemplate::New(isolate);
obj_template->Set(v8pp::to_v8(isolate, "f1"), v8::FunctionTemplate::New(isolate, A::f1));
}
//js
a.f1(); //success
a.f2();// throw exception: "a.f2 is not a function". I need that V8 will invoke my callback there instead the exception
There is no way to automatically map JavaScript method/property lookups to C++ method/property names. One reason for this is that regular Release-mode C++ binaries don't even contain those names any more. This is usually not a problem/limitation because C++ classes are statically known at compile time, they can't be extended dynamically later.
I can think of two cases where you don't have statically known classes, and corresponding approaches to solve them:
If you know the superset of possibly existing functions, you can register them all on the ObjectTemplate, and then when a method is called, check whether it actually exists, and otherwise return undefined (or whatever) from your C++ callback. This might work well e.g. if you have a set of subclasses, where any one of them can get instantiated to fill the role of the C++ object.
If you want to expose an arbitrary key/value structure (e.g. a database, or something that was parsed from JSON, etc) as an object to JavaScript, where you truly have no idea at compile time what might be valid keys, then you can use V8's interceptors API. That essentially boils down to telling V8 "whenever any property is requested from this object, first ask this callback whether that property exists". Of course in the interceptor callback, you then need a way to find out whether the property (f2 in your example) exists.
This is for a "game engine" as a bit of programming practice. All of my GameObjects are component based, where each component adds a functionality to its parent GameObject and all of these components descend from a base Component class which has virtual methods making it polymorphic.
When I read in these gameobject definitions from an XML file some components need to know about others for example a physics component needs to be aware of the transform component for the physics calculations. However if these components aren't present in the XML file then occasionally it throws up nasty null-pointers and endless rabbit hole call stack chasing to find the XML typo I fudged while half asleep.
My solution was to have a node in the XML file as an assertion that a component of this type should exist and possibly throw an exception or another appropriate action if it doesnt.
Eg.
<ComponentRequirement type="ComponentTransform">myTransformComponent</ComponentRequirement>
So I need a way of representing this in C++. The first idea, template classes to change according to what type of component it's the proxy of since this class needs to act like their unproxied component. I've solved that with some operator overloading so long as the class is a template class.
template <class T>
class ComponentRequirement {
public:
T* operator->() { (I chose the arrow operator because the CompReq class will never be referenced as a pointer)
return this->component;
}
//Other unrelated functions . . .
private:
T* component;
};
And this is all fine and dandy at compile time because I can just say
ComponentRequirement<ComponentTransform> req = ComponentRequirement("myComponentTransform");
But I need to be able to specify what that template type in place of the will be from a string when I read the XML in. I thought a hashmap could do it but I dont think the type name even "is" anything other than a human readable compiler hint so I couldn't use it as a hashmap value.
Can this be done and how could I go about implementing it? Inserting some string literal into a "black-box of magic" and get something that can be used as a template argument. And if it helps, everything that will be the value of "T" is polymorphic.
Or is there a better solution to my problem. It needs to be able to act as any Component I put into it and it needs to be discernable at runtime.
EDIT 1:
In my components I have a read and write function. If I read the component requirement in there I can make sure the template has the right value because each component is seperate.
I can then evaluate the requirements with a virtual function and a few functions in the gameobject class to check it's a valid configuration. This could solve the problem.
At a first glance I would use the factory pattern for your problem. That way you can create classes to create your objects given a different string without specifying the exact class you need at compile time unlike with normal typed constructors. The analogy I see people use are Virtual Constructors.
http://www.oodesign.com/factory-pattern.html
In essence you would have a map of factories (creator objects).
Define some top level interface for your components, such as IComponent.
Define a factory class for every component you want to generate that has a Create Instance method. I recommend the Create Instance method should be part of an interface like IFactory.
During setup of your program create your map and assign factories to particular keys. ActorCreator["MyComponent"] = new MyComponentFactory();
When you want to create an object read from an XML node you can just call the create instance method on the returned factory for the key. auto myComponent = ActorCreator[readXML]->CreateInstance();
You now have an actor/components whose concrete type has been decided at runtime instead of compile time.
I had a script with:
Custom language used only for data
Was loaded using a Script class from C++
I had tags like Type, etc
An interface to get a value for a tag - Script::GetValue(Tag, T& value)
The script was used like this:
Script* script("someFile");
script->GetValue("Type", type);
Object* obj = CreateObject(type);
obj->Load(script);
Where Load functions from object was used to load the rest of obj parameters.
Now I changed the script language to lua. My questions is:
Should I keep this way of creating objects(use lua only for data) or should I expose the factory in lua and use it from lua, something like this(in lua):
CreateObject("someType")
SetProperty(someObj, someProperty, someValue)
First of all I want to know which is faster, first or second approach. Do you have other suggestions? Because I'm refactoring this part I'm open to other suggestions. I want to keep lua because is fast, easy to integrate, and small.
You may allow your script environment to create C++ objects or not, depending on your needs.
tolua++ uses all the metatable features to allow a very straightforward manipulation of your c++ types in lua.
For example, this declaration :
// tolua_begin
class SomeCppClass
{
public:
SomeCppClass();
~SomeCppClass();
int some_field;
void some_method();
};
// tolua_end
Will automatically generate the lua bindings to allow this lua scipt :
#!lua
-- obj1 must be deleted manually
local obj1 = SomeCppClass:new()
-- obj1 will be automatically garbage collected
local obj2 = SomeCppClass:new_local()
obj1.some_field = 3 -- direct access to "some_field"
obj2:some_method() -- direct call to "some_method"
obj1:delete()
The advantage of this technique is that your lua code will ve very consistent with the relying C++ code. See http://www.codenix.com/~tolua/tolua++.html
In situations like that, I prefer to setup a bound C function that takes a table of parameters as an argument. So, the Lua script would look like the following.
CreateObject{
Type = "someType"'
someProperty = someValue,
-- ...
}
This table would be on top of the stack in the callback function, and all parameters can be accessed by name using lua_getfield.
You may also want to investigate sandboxing your Lua environment.
The first approach would most likely be faster, but the second approach would probably result in less object initialization code (assuming you're initializing a lot of objects). If you choose the first approach, you can do it manually. If you choose the second approach you might want to use a binding library like Luabind to avoid errors and speed up implementation time, assuming you're doing this for multiple object types and data types.
The simplest approach will probably be to just use Lua for data; if you want to expose the factory and use it via Lua, make sure it's worth the effort first.
I'm working on a little simulation project which uses Lua to drive the behavior of individual units (ants) and using Luabind to glue the C++ and Lua sides together. Each individual ant (there are different types, derived from the base class Ant) has a Run() function, which calls the appropriate script; the script then carries out whatever actions need to be taken, calling the exposed class functions and possibly free functions. I've gotten the Run function (in C++) to successfully execute the matching Run function in the Lua script (which just prints some text at the moment).
void AntQueen::Run()
{
lua->GetObject("QueenRun")(GetID());
}
lua is just a manager class which retrieves the function from the script. The above is calling the following in a Lua file:
function QueenRun(ID)
print("The Queen is running!")
print(ID)
end
And Luabind registration looks like this for the AntQueen class:
void Register(lua_State *luaState)
{
using namespace luabind;
module(luaState)
[
class_<AntQueen, Ant>("AntQueen")
.def("Eat", &AntQueen::Eat)
.def("ExtractLarvae", &AntQueen::ExtractLarvae)
.def("GetMaxLarvaeProduced", &AntQueen::GetMaxLarvaeProduced)
.def("GetNumAvailLarvae", &AntQueen::GetNumAvailLarvae)
];
}
The way it's set up now, ants are created, removed, and found through a simple factory/manager. Each ant can be retrieved by calling static Ant* AntFactory::GetAntByID(const int ID) which just finds the ant in a hash map and returns a pointer to the ant. What I'm trying to do is get Lua to be able to do something like the following:
function QueenRun(ID)
ant = GetAntByID(ID)
larvae = ant:GetNumAvailLarvae()
print(larvae)
ant:Eat()
end
The above is just a made up example, but hopefully it shows what I'm trying to achieve. I don't want Lua to garbage collect the objects, because they are managed already on the C++ side. While testing everything out, any attempt to do the following:
ant = GetAntByID(ID)
in Lua resulted in abort() being called and the program crashing and burning.
R6010
-abort() has been called
I just seem to be missing something with how everything gets shuttled back and forth (this is my first foray into gluing Lua and C++ together beyond toy programs). I'm pretty sure passing a plain pointer isn't the way to do it; lightuserdata seems to be what I'm looking for, but it also has a bunch of restrictions.
So to sum up: What is going on here that causes abort to be called and how can I use Luabind/the Lua C API to get a pointer to a C++ object passed to Lua and call member functions on that pointer as if it were an object (without letting Lua garbage collect it)?
The solution to this problem seemed to be tied to the AntFactory class/member functions being static. As soon as I switched from registering and using this:
//C++
static int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
to an instantiated object and regular member functions like this:
//C++
int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
everything worked with no problems at all (after also pushing the factory to the global table). I think the reason for this is that trying to call static member functions on a non-instantiated C++ object fails due to Lua (or Luabind?) not being able to have an object to query for calls.