Popularity
5.1
Growing
Activity
5.5
-
873
33
118

Description

Header-only library to expose C++ classes and functions into V8 to use them in JavaScript code. v8pp uses heavy template metaprogramming and variadic template parameters which requires modern compiler with C++11 support.

Code Quality Rank: L4
Programming language: C++
License: GNU General Public License v3.0 or later
Tags: Containers     Scripting     C++11     Header Only     V8     Bindings     Node.js Native Addon    
Latest version: v1.7.0

v8pp alternatives and similar libraries

Based on the "Scripting" category.
Alternatively, view v8pp alternatives based on common mentions on social networks and blogs.

Do you think we are missing an alternative of v8pp or a related project?

Add another 'Scripting' Library

README

Build status NPM Join the chat at https://gitter.im/pmed/v8pp

v8pp

Header-only library to expose C++ classes and functions into V8 to use them in JavaScript code. v8pp requires a compiler with C++14 support. The library has been tested on:

  • Microsoft Visual C++ 2019 (Windows 10)
  • GCC 5.4.0 (Ubuntu 16.04)
  • Clang 5.0.0 (Ubuntu 16.04)

Building and testing

The library has a set of tests that can be configured, built, and run with CMake:

~/v8pp$ mkdir out; cd out
~/v8pp/out$ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON ..
~/v8pp/out$ make
~/v8pp/out$ ctest -V

The full list of project options can be listed with cmake command:

~/v8pp/out$ cmake -LH ..

Some of them could be:

// Build documentation BUILD_DOCUMENTATION:BOOL=OFF

// Build shared library BUILD_SHARED_LIBS:BOOL=ON

// Build and run tests BUILD_TESTING:BOOL=OFF

// Header-only library V8PP_HEADER_ONLY:BOOL=0

// v8::Isolate data slot number, used in v8pp for shared data V8PP_ISOLATE_DATA_SLOT:STRING=0

// v8pp plugin initialization procedure name V8PP_PLUGIN_INIT_PROC_NAME:STRING=v8pp_module_init

// v8pp plugin filename suffix V8PP_PLUGIN_SUFFIX:STRING=.dylib

// Use std::string_view V8PP_USE_STD_STRING_VIEW:BOOL=0

// Use new V8 ABI with V8_COMPRESS_POINTERS and V8_31BIT_SMIS_ON_64BIT_ARCH V8_COMPRESS_POINTERS:BOOL=ON

Binding example

v8pp supports V8 versions after 6.3 with v8::Isolate usage in API. There are 2 targets for binding:

  • v8pp::module, a wrapper class around v8::ObjectTemplate
  • v8pp::class_, a template class wrapper around v8::FunctionTemplate

Both of them require a pointer to v8::Isolate instance. They allows to bind from C++ code such items as variables, functions, constants with a function set(name, item):

v8::Isolate* isolate;

int var;
int get_var() { return var + 1; }
void set_var(int x) { var = x + 1; }

struct X
{
    X(int v, bool u) : var(v) {}
    int var;
    int get() const { return var; }
    void set(int x) { var = x; } 
};

// bind free variables and functions
v8pp::module mylib(isolate);
mylib
    // set read-only attribute
    .set_const("PI", 3.1415)
    // set variable available in JavaScript with name `var`
    .set("var", var)
    // set function get_var as `fun`
    .set("fun", &get_var)
    // set property `prop` with getter get_var() and setter set_var()
    .set("prop", property(get_var, set_var));

// bind class
v8pp::class_<X> X_class(isolate);
X_class
    // specify X constructor signature
    .ctor<int, bool>()
    // bind variable
    .set("var", &X::var)
    // bind function
    .set("fun", &X::set)
    // bind read-only property
    .set("prop", property(&X::get));

// set class into the module template
mylib.set("X", X_class);

// set bindings in global object as `mylib`
isolate->GetCurrentContext()->Global()->Set(
    v8::String::NewFromUtf8(isolate, "mylib"), mylib.new_instance());

After that bindings will be available in JavaScript:

mylib.var = mylib.PI + mylib.fun();
var x = new mylib.X(1, true);
mylib.prop = x.prop + x.fun();

Node.js and io.js addons

The library is suitable to make Node.js and io.js addons. See [addons](docs/addons.md) document.


void RegisterModule(v8::Local<v8::Object> exports)
{
    v8pp::module addon(v8::Isolate::GetCurrent());

    // set bindings... 
    addon
        .set("fun", &function)
        .set("cls", my_class)
        ;

    // set bindings as exports object prototype
    exports->SetPrototype(addon.new_instance());
}

v8pp also provides

  • v8pp - a static library to add several global functions (load/require to the v8 JavaScript context. require() is a system for loading plugins from shared libraries.
  • test - A binary for running JavaScript files in a context which has v8pp module loading functions provided.

v8pp module example

#include <iostream>

#include <v8pp/module.hpp>

namespace console {

void log(v8::FunctionCallbackInfo<v8::Value> const& args)
{
    v8::HandleScope handle_scope(args.GetIsolate());

    for (int i = 0; i < args.Length(); ++i)
    {
        if (i > 0) std::cout << ' ';
        v8::String::Utf8Value str(args[i]);
        std::cout <<  *str;
    }
    std::cout << std::endl;
}

v8::Local<v8::Value> init(v8::Isolate* isolate)
{
    v8pp::module m(isolate);
    m.set("log", &log);
    return m.new_instance();
}

} // namespace console

Turning a v8pp module into a v8pp plugin

V8PP_PLUGIN_INIT(v8::Isolate* isolate)
{
    return console::init(isolate);
}

v8pp class binding example

#include <v8pp/module.hpp>
#include <v8pp/class.hpp>

#include <fstream>

namespace file {

bool rename(char const* src, char const* dest)
{
    return std::rename(src, dest) == 0;
}

class file_base
{
public:
    bool is_open() const { return stream_.is_open(); }
    bool good() const { return stream_.good(); }
    bool eof() const { return stream_.eof(); }
    void close() { stream_.close(); }

protected:
    std::fstream stream_;
};

class file_writer : public file_base
{
public:
    explicit file_writer(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        if (args.Length() == 1)
        {
            v8::String::Utf8Value str(args[0]);
            open(*str);
        }
    }

    bool open(char const* path)
    {
        stream_.open(path, std::ios_base::out);
        return stream_.good();
    }

    void print(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        v8::HandleScope scope(args.GetIsolate());

        for (int i = 0; i < args.Length(); ++i)
        {
            if (i > 0) stream_ << ' ';
            v8::String::Utf8Value str(args[i]);
            stream_ << *str;
        }
    }

    void println(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        print(args);
        stream_ << std::endl;
    }
};

class file_reader : public file_base
{
public:
    explicit file_reader(char const* path)
    {
        open(path);
    }

    bool open(const char* path)
    {
        stream_.open(path, std::ios_base::in);
        return stream_.good();
    }

    v8::Local<v8::Value> getline(v8::Isolate* isolate)
    {
        if ( stream_.good() && ! stream_.eof())
        {
            std::string line;
            std::getline(stream_, line);
            return v8pp::to_v8(isolate, line);
        }
        else
        {
            return v8::Undefined(isolate);
        }
    }
};

v8::Local<v8::Value> init(v8::Isolate* isolate)
{
    v8::EscapableHandleScope scope(isolate);

    // file_base binding, no .ctor() specified, object creation disallowed in JavaScript
    v8pp::class_<file_base> file_base_class(isolate);
    file_base_class
        .set("close", &file_base::close)
        .set("good", &file_base::good)
        .set("is_open", &file_base::is_open)
        .set("eof", &file_base::eof)
        ;

    // .ctor<> template arguments declares types of file_writer constructor
    // file_writer inherits from file_base_class
    v8pp::class_<file_writer> file_writer_class(isolate);
    file_writer_class
        .ctor<v8::FunctionCallbackInfo<v8::Value> const&>()
        .inherit<file_base>()
        .set("open", &file_writer::open)
        .set("print", &file_writer::print)
        .set("println", &file_writer::println)
        ;

    // .ctor<> template arguments declares types of file_reader constructor.
    // file_base inherits from file_base_class
    v8pp::class_<file_reader> file_reader_class(isolate);
    file_reader_class
        .ctor<char const*>()
        .inherit<file_base>()
        .set("open", &file_reader::open)
        .set("getln", &file_reader::getline)
        ;

    // Create a module to add classes and functions to and return a
    // new instance of the module to be embedded into the v8 context
    v8pp::module m(isolate);
    m.set("rename", &rename)
     .set("writer", file_writer_class)
     .set("reader", file_reader_class)
        ;

    return scope.Escape(m.new_instance());
}

} // namespace file

V8PP_PLUGIN_INIT(v8::Isolate* isolate)
{
    return file::init(isolate);
}

Creating a v8 context capable of using require() function

#include <v8pp/context.hpp>

v8pp::context context;
context.set_lib_path("path/to/plugins/lib");
// script can now use require() function. An application
// that uses v8pp::context must link against v8pp library.
v8::HandleScope scope(context.isolate());
context.run_file("some_file.js");

Using require() from JavaScript

// Load the file module from the class binding example and the
// console module.
var file    = require('file'),
    console = require('console')

var writer = new file.writer("file")
if (writer.is_open()) {
    writer.println("some text")
    writer.close()
    if (! file.rename("file", "newfile"))
        console.log("could not rename file")
}
else console.log("could not open `file'")

console.log("exit")

Create a handle to an externally referenced C++ class.

// Memory for C++ class will remain when JavaScript object is deleted.
// Useful for classes you only wish to inject.
typedef v8pp::class_<my_class> my_class_wrapper;
v8::Local<v8::Value> val = my_class_wrapper::reference_external(isolate, &my_class::instance());
// Assuming my_class::instance() returns reference to class

Import externally created C++ class into v8pp.

// Memory for c++ object will be reclaimed by JavaScript using "delete" when
// JavaScript class is deleted.
typedef v8pp::class_<my_class> my_class_wrapper;
v8::Local<v8::Value> val = my_class_wrapper::import_external(isolate, new my_class);

Compile-time configuration

The library uses several preprocessor macros, defined in v8pp/config.hpp file:

  • V8PP_ISOLATE_DATA_SLOT - A v8::Isolate data slot number, used to store v8pp internal data
  • V8PP_PLUGIN_INIT_PROC_NAME - Plugin initialization procedure name that should be exported from a v8pp plugin.
  • V8PP_PLUGIN_SUFFIX - Plugin filename suffix that would be added if the plugin name used in require() doesn't end with it.
  • V8PP_HEADER_ONLY - Use header-only implemenation, enabled by default.

v8pp alternatives