Use Node. js to write the basic Extension Method for other programs _ node. js

Source: Internet
Author: User


This article describes how to use Node. js to compile extensions for other programs. The example in this article is to use Node to allow JavaScript code to interact with C ++ applications. For more information, see Start preparation


First, we use the following directory structure to create a node notification folder.

The Code is as follows:


.

| -- Build/# This is where our extension is built.

| -- Demo/

| '-- Demo. js # This is a demo Node. js script to test our extension.

| -- Src/

| '-- Node_gtknotify.cpp # This is the where we do the mapping from C ++ to Javascript.

'-- Wscript # This is our build configuration used by node-waf


This pretty tree is generated using a common tree.

Now let me create the test script demo. js and determine that the API we expanded should look like in the early stage:


// This loads our extension on the notify variable.

// It will only load a constructor function, notify.notification().

var notify = require("../build/default/gtknotify.node"); // path to our extension


var notification = new notify.notification();

notification.title = "Notification title";

notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16

notification.send("Notification message");



Compile our Node. js Extension

Init Method

To create a Node. js extension, we need to write a C ++ class that inherits node: ObjectWrap. ObjectWrap makes it easier for us to interact with Javascript.

Let's first write the basic framework of the class:



# Include


 

// V8 is the Javascript engine used by QNode # include

 

 

// We will need the following libraries for our GTK + notification # include# Include

   

   

# Include

   

     

Using namespace v8; class gtknotted: node: ObjectWrap {private: public: gtknotted (){}~ Gtknotted () {} static void Init (HandleTarget) {// This is what Node will call when we load the extension through require (), see boilerplate code below .}}; /** WARNING: Boilerplate code ahead. ** See https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/ & http://www.freebsd.org/cgi/man.cgi? Query = dlsym ** Thats it for actual interfacing with v8, finally we need to let Node. js know how to dynamically load our code. * Because a Node. js extension can be loaded at runtime from a shared object, we need a symbol that the dlsym function can find, * so we do the following: */v8: PersistentGtknotted: persistent_function_template; extern "C" {// Cause of name mangling in C ++, we use extern C here static void init (HandleTarget) {gtknotted: Init (target);} // @ see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101 NODE_MODULE (gtknotted, init );}


Now, we must write the following code into our Init () method:

Declare the constructor and bind it to our target variable. Var n = require ("notification"); binds notification () to n: n. notification ().




// Wrap our C++ New() method so that it's accessible from Javascript  // This will be called by the new operator in Javascript, for example: new notification();  v8::Locallocal_function_template = v8::FunctionTemplate::New(New);     // Make it persistent and assign it to persistent_function_template which is a static attribute of our class.  Gtknotify::persistent_function_template = v8::Persistent::New(local_function_template);     // Each JavaScript object keeps a reference to the C++ object for which it is a wrapper with an internal field.  Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object  // Set a "class" name for objects created with our constructor  Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));     // Set the "notification" property of our target variable and assign it to our constructor function  target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());




Declared attributes: n. title and n. icon.




 // Set property accessors  // SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);  // For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()



Declared Prototype Method: n. send ()




 // This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34)  // Arguments: our constructor function, Javascript method name, C++ method name  NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);



Now our Init () method looks like this:



// Our constructorstatic v8: PersistentPersistent_function_template; static void Init (HandleTarget) {v8: HandleScope scope; // used by v8 for garbage collection // Our constructor v8: LocalLocal_function_template = v8: FunctionTemplate: New (New); Gtknotify: persistent_function_template = v8: Persistent: New (local_function_template); Gtknotify: persistent_function_template-> InstanceTemplate ()-> SetInternalFieldCount (1); // 1 since this is a constructor gtkno.pdf :: plugin-> SetClassName (v8: String: NewSymbol ("Notification"); // Our getters and setters Gtknotify: persistent_function_template-> InstanceTemplate ()-> SetAccessor (String:: New ("title"), GetTitle, SetTitle); Gtknotify: persistent_function_template-> InstanceTemplate ()-> SetAccessor (String: New ("icon"), GetIcon, setIcon); // Our methods NODE_SET_PROTOTYPE_METHOD (Gtknotify: persistent_function_template, "send", Send); // Binding our constructor function to the target variable target-> Set (String :: newSymbol ("notification"), Gtknotify: persistent_function_template-> GetFunction ());}


The rest is to write the C ++ method we use in the Init method: New, GetTitle, SetTitle, GetIcon, SetIcon, Send

Constructor method: New ()

The New () method creates a New instance of our custom class (A gtknotted object), sets some initial values, and then returns the JavaScript processing of the object. This is the expected behavior of JavaScript to call the constructor using the new operator.



Std: string title; std: string icon; // new notification () static HandleNew (const Arguments & args) {HandleScope scope; Gtknotify * gtknotify_instance = new Gtknotify (); // Set some default values gtknotify_instance-> title = "Node. js? 1.1.9 "; gtknotify_instance-> icon =" terminal "; // Wrap our C ++ object as a Javascript object gtknotify_instance-> Wrap (args. this (); return args. this ();} getters and setters: GetTitle (), SetTitle (), GetIcon (), SetIcon ()



Below are some sample code that can be attributed to the value conversion between C ++ and JavaScript (v8.



// this.titlestatic v8::HandleGetTitle(v8::Localproperty, const v8::AccessorInfo& info) { // Extract the C++ request object from the JavaScript wrapper. Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder()); return v8::String::New(gtknotify_instance->title.c_str());}// this.title=static void SetTitle(Localproperty, Localvalue, const AccessorInfo& info) { Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder()); v8::String::Utf8Value v8str(value); gtknotify_instance->title = *v8str;}// this.iconstatic v8::HandleGetIcon(v8::Localproperty, const v8::AccessorInfo& info) { // Extract the C++ request object from the JavaScript wrapper. Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder()); return v8::String::New(gtknotify_instance->icon.c_str());}// this.icon=static void SetIcon(Localproperty, Localvalue, const AccessorInfo& info) { Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder()); v8::String::Utf8Value v8str(value); gtknotify_instance->icon = *v8str;}



Prototype Method: Send ()

First, we extract this reference of the C ++ object, and then use the attributes of the object to build notifications and display them.



// this.send()static v8::HandleSend(const Arguments& args) { v8::HandleScope scope; // Extract C++ object reference from "this" Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(args.This());  // Convert first argument to V8 String v8::String::Utf8Value v8str(args[0]);  // For more info on the Notify library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html Notify::init("Basic"); // Arguments: title, content, icon Notify::Notification n(gtknotify_instance->title.c_str(), *v8str, gtknotify_instance->icon.c_str()); // *v8str points to the C string it wraps // Display the notification n.show(); // Return value return v8::Boolean::New(true);}



Compilation Extension

Node-waf is a construction tool used to compile Node extensions, which is the basic encapsulation of waf. The building process can be configured through a file named wscript.



def set_options(opt): opt.tool_options("compiler_cxx") def configure(conf): conf.check_tool("compiler_cxx") conf.check_tool("node_addon") # This will tell the compiler to link our extension with the gtkmm and libnotifymm libraries. conf.check_cfg(package='gtkmm-2.4', args='--cflags --libs', uselib_store='LIBGTKMM') conf.check_cfg(package='libnotifymm-1.0', args='--cflags --libs', uselib_store='LIBNOTIFYMM') def build(bld): obj = bld.new_task_gen("cxx", "shlib", "node_addon") obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"] # This is the name of our extension. obj.target = "gtknotify" obj.source = "src/node_gtknotify.cpp" obj.uselib = ['LIBGTKMM', 'LIBNOTIFYMM']



Now we are ready to start building. Run the following command in the top-level directory:


Node-waf configure & node-waf build

If everything is normal, we will get the compiled extension, which is located at:./build/default/gtknotted. node. Let's try:



$ node> var notif = require('./build/default/gtknotify.node');> n = new notif.notification();{ icon: 'terminal', title: 'Node.js' }> n.send("Hello World!");true



The above code will display a notification message in the upper-right corner of your screen.

Pack into npm package

This is cool, but how do you share your efforts with the Node community? This is the main purpose of npm: to make it easier to expand and distribute.

Creating an npm extension package is very simple. All you need to do is create a file package. json that contains your extension information in your top-level directory:



{// Extension name (do not include node or js in the name, which is an implicit keyword ). // This is the name of the extension imported through require. "name": "notify", // Version shocould be http://semver.org/compliant "version": "v0.1.0" // These scripts will run when npm installation and npm uninstallation are called ., "scripts": {"preinstall": "node-waf configure & node-waf build", "preuninstall ": "rm-rf build/*"} // This is the relative path to build our extension ., "main": "build/default/gtknotted. node "// the following is an optional field:," description ":" Description of the extension .... "," homepage ":" https://github.com/olalonde/node-notify "," author ": {" name ":" Olivier Lalonde "," email ":" olalonde@gmail.com "," url ": "http://www.syskall.com/"}, "repository": {"type": "git", "url": "https://github.com/olalonde/node-notify.git "}}



For more details about the package. json format, you can use npm help json to obtain the document. Note that most fields are optional.


Now you can run npm install in your top-level directory to install your new npm package. if everything goes well, you can simply load your extension var require y = require ('your package name ');. you can use this command to create a link to your Development Directory. When your code changes, you do not have to install or uninstall it every time.

If you have written a cool extension, you may want to publish it to the Internet in the central npm library. First, you need to create an account:




$ npm adduser



Next, return to your root directory encoding and run:



$ npm publish



In this way, your package can now be installed by anyone using the npm install your package name command.




   





Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.