Use Node. js to write basic extension methods for other programs

Source: Internet
Author: User

Use Node. js to write basic extension methods for other programs

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:

?

1

2

3

4

5

6

7

8 // This loads our extension on the specified y variable.

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

Var every Y = require ("../build/default/gtknotify. node"); // path to our extension

Var notification = new every Y. 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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37 # 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 Gtknotify: node: ObjectWrap {

Private:

Public:

Gtknotted (){}

~ Gtknotted (){}

Static void Init (Handle target ){

// 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: Persistent Gtknotify: persistent_function_template;

Extern "C" {// Cause of name mangling in C ++, we use extern C here

Static void init (Handle target ){

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 ().

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14 // 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: Local Local_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.

?

1

2

3

4

5 // 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 ()

?

1

2

3 // 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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22 // Our constructor

Static v8: Persistent Persistent_function_template;

Static void Init (Handle target ){

V8: HandleScope scope; // used by v8 for garbage collection

// Our constructor

V8: Local Local_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 function

Gtknotify: persistent_function_template-> 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.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18std: string title;

Std: string icon;

// New notification ()

Static Handle New (const Arguments & args ){

HandleScope scope;

Gtknotify * gtknotify_instance = new Gtknotify ();

// Set some default values

Gtknotify_instance-> title = "Node. js ";

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.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24 // this. title

Static v8: Handle GetTitle (v8: Local Property, 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 (Local Property, Local Value, const AccessorInfo & info ){

Gtknotify * gtknotify_instance = node: ObjectWrap: Unwrap (Info. Holder ());

V8: String: Utf8Value v8str (value );

Gtknotify_instance-> title = * v8str;

}

// This. icon

Static v8: Handle GetIcon (v8: Local Property, 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 (Local Property, Local Value, 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.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18 // this. send ()

Static v8: Handle Send (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 specified y library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html

Y: init ("Basic ");

// Arguments: title, content, icon

Required Y: 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.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17def 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 libpolicymm libraries.

Conf. check_cfg (package = 'gtkmm-2.4 ', args =' -- cflags -- libs', uselib_store = 'libgtkmm ')

Conf. check_cfg (package = 'libpolicymm-8080', args = '-- cflags -- libs', uselib_store = 'libpolicymm ')

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.tar get = "gtknotted"

Obj. source = "src/node_gtknotify.cpp"

Obj. uselib = ['libgtkmm', 'libpolicymm']

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:

?

1

2

3

4

5

6 $ node

> Var notif = require ('./build/default/gtknotted. 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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35 {

// Extension name (do not include node or js in the name. This is an implicit keyword ).

// This is the name of the extension imported through require.

"Name": "policy ",

// Version shocould be http://semver.org/compliant

"Version": "v0.1.0"

// These scripts will be 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 are optional fields:

, "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:

?

1 $ npm adduser

Next, return to your root directory encoding and run:

?

1

2 $ 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.