Wrapping a C++ Object to a v8 Object in a Node Addon
c++, node.js, v8
Solution
As long as the `Foobar` instances have been created using your `nodeNew` constructor, or more specifically as long as `Wrap` has been called at some point on the object, you should be able to do this:
v8::Local<v8::Array> foobarsArray = v8::Array::New(foobarCount);
for(unsigned int i = 0; i < foobarCount; i++){
foobarsArray->Set(i, (*foobars)[i]->handle_);
}
return scope.Close(foobarsArray);
Also Node has some helpers to shorten your `Init`:
// Prototype functions
node::SetPrototypeMethod(tpl, "getID", nodeGetID);
node::SetPrototypeMethod(tpl, "getVelocity", nodeGetVelocity);
node::SetPrototypeMethod(tpl, "getState", nodeGetState);
In your case since you aren't directly creating the `Foobar` instances in JS, you'd want to save a reference to the constructor function, and use that to create new instances, rather than calling `new Foobar`. If you consider the code you have written, `Foobar` has very little knowledge of the JavaScript side of things, as it is the v8 constructor object that knows how to make an object with the proper methods.
// Save a global reference
v8::Persistent<v8::Function> foobarConstructor;
// Inside 'Init', assign the constructor to the global
foobarConstructor = v8::Persistent<v8::Function>::New(tpl->GetFunction());
target->Set(v8::String::NewSymbol("Foobar"), foobarConstructor);
// Then instead of 'Foobar* instance = new Foobar'
int argc = 4;
Local<Value> argv[] = {
// The 4 arguments.
};
v8::Local<v8::Object> instance = foobarConstructor->NewInstance(argc, argv);
Problem
I'm currently writing a Node addon in C++, and I'm running into an issue where I need to make and return a v8 Array filled with v8 wrapped C++ object instances. At the moment, the code looks something like this ``` v8::Handle<v8::Value> Controller::nodeArray(const v8::Arguments& args) { v8::HandleScope scope; Controller* controller= ObjectWrap::Unwrap<Controller>(args.This()); const std::vector<Foobar*>* foobars = controller->getFoobars(); unsigned int foobarCount = foobars->size(); v8::Handle<v8::Array> foobarsArray = v8::Array::New(foobarCount); std::vector<Foobar*>::const_iterator foobar = foobars->begin(); for(unsigned int i = 0; i < foobarCount; i++) { // Need to create a v8 Object that wraps a single instance of a // Foobar object in "foobars" // Then push the object to the v8 Array? // foobarsArray->Set(i, [some Foobar instance]); snake++; } return foobarsArray; } ``` I've tried several different things in the `for` loop to no avail, however. How exactly should I go about this? Foobar and SuperFoo Here are the `Init` initializer and `nodeNew` constructor functions defined in `Foobar`, as well as the header files for `Foobar` and `SuperFoo`. ``` #define BUILDING_NODE_EXTENSION #ifndef FOOBAR_H_ #define FOOBAR_H_ #include <algorithm> #include <cmath> #include <cstdlib> #include <ctime> #include <node.h> #include <string> #include <vector> #include <iostream> #include "EnvironmentObject.h" #include "SuperFoo.h" #include "Grid.h" #include "GridSection.h" #include "Point.h" #include "Teams.h" #include "Vector.h" class SuperFoo; class Grid; class Foobar : public SuperFoo { public: Foobar(); virtual ~Foobar(); Foobar(int id, const Point& location, const int& team, const Grid& world); Foobar(const Foobar& snake); // Node Implementation static void Init(v8::Handle<v8::Object> target); private: // Node Implementation static v8::Handle<v8::Value> nodeNew(const v8::Arguments& args); static v8::Handle<v8::Value> nodeGetID(const v8::Arguments& args); static v8::Handle<v8::Value> nodeGetTeam(const v8::Arguments& args); static v8::Handle<v8::Value> nodeGetState(const v8::Arguments& args); static v8::Handle<v8::Value> nodeGetVelocity(const v8::Arguments& args); int id_; int team_; int state_; Vector* velocity_; const Grid* world_; }; #endif /* FOOBAR_H_ */ ``` ``` #define BUILDING_NODE_EXTENSION #ifndef GAMEOBJECT_H_ #define GAMEOBJECT_H_ #include <string> #include <node.h> #include "Point.h" #include "Vector.h" class SuperFoo : public node::ObjectWrap { public: SuperFoo() : collidable_(true), stationary_(true) {} virtual ~SuperFoo(); SuperFoo(const std::string& type, const Point& position); SuperFoo(const SuperFoo& object); virtual bool collide(SuperFoo& object) = 0; Point getPosition() const; std::string getType() const; void setPosition(const Point& position); bool isCollidable() const; bool isStationary() const; void setCollidable(bool coolide); void setStationary(bool stationary); protected: Point* position_; private: // Node Implementation static v8::Handle<v8::Value> nodeGetPosition(const v8::Arguments& args); std::string* type_; bool collidable_; bool stationary_; }; #endif /* SUPERFOO_H_ */ ``` ``` void Foobar::Init(v8::Handle<v8::Object> target) { // Prepare constructor template v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(nodeNew); tpl->SetClassName(v8::String::NewSymbol("Foobar")); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype functions tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("getID"), v8::FunctionTemplate::New(nodeGetID)->GetFunction()); tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("getVelocity"), v8::FunctionTemplate::New(nodeGetVelocity)->GetFunction()); tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("getState"), v8::FunctionTemplate::New(nodeGetState)->GetFunction()); v8::Persistent<v8::Function> constructor = v8::Persistent<v8::Function>::New(tpl->GetFunction()); target->Set(v8::String::NewSymbol("Foobar"), constructor); } v8::Handle<v8::Value> Foobar::nodeNew(const v8::Arguments& args) { v8::HandleScope scope; int id = args[0]->NumberValue(); Point* position = ObjectWrap::Unwrap<Point>(args[1]->ToObject()); int team = args[2]->NumberValue(); Grid* world = ObjectWrap::Unwrap<Grid>(args[3]->ToObject()); Foobar* foobar = new Foobar(id, *position, team, *world); foobar->Wrap(args.This()); return args.This(); } ``` ``` v8::Handle<v8::Value> Controller::nodeSpawnFoobar(const v8::Arguments& args) { Controller* gridController = ObjectWrap::Unwrap<Controller>(args.This()); int team = args[0]->NumberValue(); gridController->createFoobar(foobarID++, team); return v8::Undefined(); } void Controller::spawnFoobar(int id, int team) { Point createPosition; if(team == Teams::kBlue) { createPosition = world_->getAreaPosition(Teams::kBlue)->getPosition(); } else if (team == Teams::kRed) { createPosition = world_->getAreaPosition(Teams::kRed)->getPosition(); } int xh = createPosition.get()[0] - EnvironmentObject::kAreaSize / 2 int yh = createPosition.get()[1] + EnvironmentObject::kAreaSize / 2; int x = (rand() % EnvironmentObject::kAreaSize) + xh; int y = (rand() % EnvironmentObject::kAreaSize) + yh - EnvironmentObject::kAreaSize; foobars_->push_back(new Foobar(id, Point(x, y), team, *world_)); } ``` Javascript Call Here's how the method is being used ``` this.update = function() { if([some event]) { controller.createFoobar(); console.log(this.getFoobars()) } } this.getFoobars = function() { var foobars = controller.getFoobars(); var foobarsArray = new Array(); for(i = 0; i < foobars.length; i++) { var foobar = foobar[i]; var position = foobar.getPosition(); var posX = position.getX(); var posY = position.getY(); var positionPoint = new Point(posX, posY); var velocity = foobar.getVelocity(); var toX = velocity.getToX(); var toY = velocity.getToX(); var velocityVector = new Vector(toX, toY); var foobarObj = { id: foobar.getID(), team: foobar.getTeam(), position: positionPoint, velocity: velocityVector, state: foobar.getState() } foobarsArray.push(foobarObj); } return miniSnakesArray; }; ```