The correct way to write the node. js plug in

  • 2020-03-30 03:39:43
  • OfStack

Node.js is worth a little more experimenting with using JavaScript to write backend effects for unclustering. But if you need some functionality that you can't use directly or even implement at all, can you introduce it from the C/C++ library? The answer is yes, all you have to do is write a plug-in that USES other code library resources in your JavaScript code. Let's begin today's inquiry together.

introduce

As node. js notes in its official documentation, plug-ins are Shared objects that are linked dynamically to interface JavaScript code with C/C++ libraries. This means that we can reference anything from the C/C++ library and incorporate it into node. js by creating a plug-in.

As an example, we will create a package for the standard STD ::string object.

The preparatory work

Before we start writing, you need to make sure you have all the material you need for subsequent module compilation. You need node-gyp and all its dependencies. You can install node-gyp with the following command:


npm install -g node-gyp

  In terms of dependencies, we need the following items for Unix systems: • Python (version 2.7 required, 3.x does not work properly)

The & # 8226; The make

The & # 8226; A C++ compiler toolchain (such as GPP or g++)

For example, on Ubuntu you can install all of the above using the following command (Python 2.7 should already be pre-installed) :


sudo apt-get install build-essentials 

In a Windows environment, what you need is:

The & # 8226; Python (version 2.7.3, 3.x does not work properly)

The & # 8226; Microsoft Visual Studio C++ 2010 (for Windows XP/Vista)

The & # 8226; Microsoft Visual Studio C++ 2012 for Windows Desktop (for Windows 7/8)

To emphasize, the Express version of Visual Studio works just as well.

Binding. Gyp file

This file is used by node-gyp to generate the appropriate build file for our plug-in. You can click here to view wikipedia's.gyp documentation, but the example we're going to use today is so simple that you can simply use the following code:


{ 
  "targets": [ 
    { 
      "target_name": "stdstring", 
      "sources": [ "addon.cc", "stdstring.cc" ] 
    } 
  ] 
}

The target_name can be set to anything you like. The sources array contains all the source files needed for the plug-in. Also included in our example is adon.cc, which holds the code necessary to compile the plug-in and stdstring.cc, plus our wrapper classes.

STDStringWrapper class

The first step is to define our own class in the stdstring.h file. If you are familiar with C++ programming, you will not be unfamiliar with the following two lines of code.


#ifndef STDSTRING_H 
#define STDSTRING_H

This is the standard include guard. Next, we need to include the following two headers:

# include 
# include
The first one is for the STD ::string class, and the second include works on everything related to Node and V8.

Once this is done, we can declare our own classes:

Class STDStringWrapper: public node::ObjectWrap {
For all the classes we intend to include in the plug-in, we must extend the node::ObjectWrap class.

Now we can start defining the private property of the class:


private: 
  std::string* s_; 
  
  explicit STDStringWrapper(std::string s = ""); 
  ~STDStringWrapper();

In addition to the constructor and the parse function, we need to define a pointer to the STD ::string. This is the core of the technique and can be used to interface the C/C++ code base to Node -- we define a private pointer to the C/C++ class and will use this pointer in all subsequent methods.

Now we declare the constructor static property, which will provide the function for the class we created in V8:

The static v8: : Persistent constructor;
Interested friends can click here to see the template description scheme for more details.

Now we also need a New method that will be assigned to the constructor mentioned earlier, and V8 will initialize our class:

Static v8::Handle New(const v8::Arguments& args);
  Each function applied to V8 should follow the following requirements: it will accept a reference to the V8 ::Arguments object and return a V8 ::Handle> V8: : Value> This is exactly how V8 always handles weakly typed JavaScript when we use strongly typed C++ coding.

After that, we need to insert two more methods into the object's prototype:


static v8::Handle add(const v8::Arguments& args); 
static v8::Handle toString(const v8::Arguments& args);

The toString() method allows us to get the value of s_ instead of [Object Object] when we use it with a normal JavaScript string.

Finally, we'll introduce an initialization method (which will be called by V8 and assigned to the constructor function) and close include guard:


public: 
    static void Init(v8::Handle exports); 
}; 
  
#endif

The role of exported objects in JavaScript modules is equivalent to that of module.exports.

Stdstring.cc files, constructors, and parsing functions

Now create the stdstring.cc file. First we need to include our header:


#include "stdstring.h" 

Here is the constructor property (because it belongs to a static function) :


v8::Persistent STDStringWrapper::constructor; 

The constructor that serves the class will assign the s_ attribute:


STDStringWrapper::STDStringWrapper(std::string s) { 
  s_ = new std::string(s); 
}

The parse function deletes it to avoid memory overflow:


STDStringWrapper::~STDStringWrapper() { 
  delete s_; 
}

Again, you have to delete everything assigned with new, because each of these situations can cause an exception, so keep that in mind or use a Shared pointer.

The Init method

This method will be invoked by V8 to initialize our class (assign constructor, to place all the content we intend to use in JavaScript in an exports object) :

Void STDStringWrapper: : Init (v8: : Handle exports) {
First, we need to create a function template for our New method:

V8: : Local TPL = v8: : FunctionTemplate: : the New (New);
This is somewhat similar to the new Function in JavaScript -- it allows us to prepare our own JavaScript classes.

Now we can name the function as needed (if you missed this step, the constructor will be anonymous, i.e., function someName() {} or function () {}) :

The TPL - > SetClassName (v8: : String: : NewSymbol (" STDString "));
We use v8::String::NewSymbol() to create a special type of String for attribute names - this saves the engine a little time.

After that, we need to specify how many fields our class instance contains:

The TPL - > InstanceTemplate () - > SetInternalFieldCount (2);
We have two methods -- add() and toString(), so we set the number to 2. Now we can add our own methods to the function prototype:

The TPL - > PrototypeTemplate () - > Set (v8: : String: : NewSymbol (" add "), v8: : FunctionTemplate: : the New (add) - > GetFunction ());  
The TPL - > PrototypeTemplate () - > Set (v8: : String: : NewSymbol "toString (), v8: : FunctionTemplate: : New toString () - > GetFunction ());
This seems like a lot of code, but if you look closely you'll see the pattern: we use tpl-> PrototypeTemplate () - > Set() to add each method. We also provide them with names and functiontemplates using v8::String::NewSymbol().

Finally, we can place the constructor in an exports object within our constructor class property:


constructor = v8::Persistent::New(tpl->GetFunction()); 
  exports->Set(v8::String::NewSymbol("STDString"), constructor); 
}

The New method

Now we have to do is define a JavaScript Object. The prototype. The constructor method of operation effect is the same:


v8::Handle STDStringWrapper::New(const v8::Arguments& args) {

  We first need to create a scope for it:


v8::HandleScope scope; 

After that, we can use the.IsConstructCall() method of the args object to check whether the constructor can be called with the new keyword:


if (args.IsConstructCall()) { 

If possible, we first pass the parameter to STD ::string as shown below:


v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str);

... So we can pass it to our wrapper class constructor:


STDStringWrapper* obj = new STDStringWrapper(s);

After that, we can use the.wrap () method (inherited from node::ObjectWrap) of the object we created earlier to assign it to this variable:


obj->Wrap(args.This()); 

Finally, we can return the newly created object:


return args.This(); 

If the function cannot be called with new, we can also call the constructor directly. Next, what we need to do is to set a constant for the parameter count:


} else { 
  const int argc = 1; 

Now we need to create an array with our own parameters:


v8::Local argv[argc] = { args[0] }; 

Then put the constructor - > The result of the NewInstance method is passed to scope.close, so that the object can function later (scope.Close basically allows you to maintain the object by moving the handle to a higher scope -- that's how functions work) :


    return scope.Close(constructor->NewInstance(argc, argv)); 
  } 
} 

The add method

Now let's create the add method, which allows you to add content to the internal STD ::string of the object:


v8::Handle STDStringWrapper::add(const v8::Arguments& args) { 

First, we need to create a scope for our function and convert the parameter to STD ::string as before:


v8::HandleScope scope; 
  
v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str); 

Now we need to unpack this object. We've done this reverse encapsulation before -- this time to get a pointer to an object from this variable.


STDStringWrapper* obj = ObjectWrap::Unwrap(args.This()); 

Then we can access the s_ property and use its.append() method:


obj->s_->append(s); 

Finally, we return the current value of the s_ attribute (again, scope.Close) :


return scope.Close(v8::String::New(obj->s_->c_str())); 

Since the v8::String::New() method can only accept a char pointer as a value, we need to use obj-> S_ - > C_str () to get it.

You should also create a build directory in your plug-in folder.

test

Now we can test our plug-in. Create a test.js file and the necessary compilation libraries in our plug-in directory (you can skip the.node extension) :


var addon = require('./build/Release/addon'); 

Next, create a new instance for our object:


var test = new addon.STDString('test'); 

Let's do something with it, like add or convert it to a string:


test.add('!'); 
console.log('test's contents: %s', test); 

After running, you should see the following results in the console:

conclusion

Practical link

Interested friends can check out the links below for more resources and details related to node. js plug-in development, V8, and the C event loop library.

The & # 8226; Node.js plug-in documentation

The & # 8226; V8 documentation

The & # 8226; Libuv (C event loop repository) from GitHub

English: (link: http://code.tutsplus.com/tutorials/writing-nodejs-addons - CMS - 21771)


Related articles: