GitHub - pmed/v8pp: Bind C++ functions and classes into V8 JavaScript engine (original) (raw)
v8pp
Header-only library to expose C++ classes and functions into V8 to use them in JavaScript code. v8pp requires a compiler with C++17 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:
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 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 aroundv8::ObjectTemplatev8pp::class_, a template class wrapper aroundv8::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
.const_("PI", 3.1415)
// set variable available in JavaScript with name var
.var("var", var)
// set function get_var as fun
.function("fun", &get_var)
// set property prop with getter get_var() and setter set_var()
.property("prop", get_var, set_var);
// bind class v8pp::class_ X_class(isolate); X_class // specify X constructor signature .ctor<int, bool>() // bind variable .var("var", &X::var) // bind function .function("fun", &X::set) // bind read-only property .property("prop",&X::get);
// set class into the module template mylib.class_("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 document.
void RegisterModule(v8::Localv8::Object exports) { v8pp::module addon(v8::Isolate::GetCurrent());
// set bindings...
addon
.function("fun", &function)
.class_("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
#include <v8pp/module.hpp>
namespace console {
void log(v8::FunctionCallbackInfov8::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::Localv8::Value init(v8::Isolate* isolate) { v8pp::module m(isolate); m.function("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
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::FunctionCallbackInfov8::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::Localv8::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
.function("close", &file_base::close)
.function("good", &file_base::good)
.function("is_open", &file_base::is_open)
.function("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>()
.function("open", &file_writer::open)
.function("print", &file_writer::print)
.function("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>()
.function("open", &file_reader::open)
.function("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.function("rename", &rename)
.class_("writer", file_writer_class)
.class_("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_wrapper; v8::Localv8::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_wrapper; v8::Localv8::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 dataV8PP_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 inrequire()doesn't end with it.V8PP_HEADER_ONLY- Use header-only implemenation, enabled by default.