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?
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_));
}
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;
};
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With