Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping a C++ Object to a v8 Object in a Node Addon

Tags:

c++

node.js

v8

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;
};
like image 558
Aetylus Avatar asked Oct 22 '22 11:10

Aetylus


1 Answers

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);
like image 187
loganfsmyth Avatar answered Oct 27 '22 09:10

loganfsmyth