Revise the old and learn the new, you can become wet
First of all, please remember this online manual of V8 - http://izs.me/v8-docs/main.html.
Remember the last building.gyp file?
The code copy is as follows:
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ]
}
]
}
Just like this, learn from one example and learn from it. If you have more *.cc files, it will be like this:
"sources": [ "addon.cc", "myexample.cc" ]
Last time we separated the two steps, and in fact, configuration and compilation can be put together:
$ node-gyp configure build
Have you finished reviewing? without? !
OK, then let's go on.
Table of contents
Function parameters
Now we are finally going to talk about parameters.
Let's imagine that there is a function add(a, b) that represents adding a and b to return the result, so write the function outer frame first:
The code copy is as follows:
#include <node.h>
using namespace v8;
Handle<Value> Add(const Arguments& args)
{
HandleScope scope;
//... Come again!
}
Arguments
This is the parameter of the function. Let’s take a look at the official manual reference of v8 first.
•int Length() const
•Local<Value> operator[](int i) const
We don’t care about the others, these two are very important! One represents the number of parameters passed in the function, and the other is the brackets that access the nth parameter through the subscript index.
So as for the above requirements, we can roughly understand that args.Length() is 2, args[0] represents a and args[1] represents b. And we must judge that the type of these two numbers must be Number.
Not sure, the index operator in the bracket returns the result of a Local<Value>, which is the base class of all types of Node.js. Therefore, if the parameters passed in are of varying type, we must judge what parameters it is. This is related to some functions of this Value type.
•IsArray()
•IsBoolean()
•IsDate()
•IsFunction()
•IsInt32()
•IsNativeError()
•IsNull()
•IsNumber()
•IsRegExp()
•IsString()
•...
I won't list them all, read the rest by myself. 。:.゚(*´∀`)ノ゚.:。
ThrowException
This is a function we will use later. Specifically, it can be found in the v8 documentation.
As the name suggests, it is throwing an error. After executing this statement, it is equivalent to executing a throw() statement in the local Node.js file. For example:
ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
It is equivalent to executing a Node.js:
throw new TypeError("Wrong number of arguments");
Undefined()
This function is also in the document.
Specifically, it is a null value, because some functions do not need to return any specific value, or do not return a value. At this time, Undefined() needs to be used instead.
Take action!
After understanding the above points, I believe you will be able to write the logic of a + b soon. I will copy the code of the Node.js official manual and read it for you. It will be done:
The code copy is as follows:
#include <node.h>
using namespace v8;
Handle<Value> Add(const Arguments& args)
{
HandleScope scope;
// It means that more than 2 parameters can be passed in, but in fact we only use the first two
if(args.Length() < 2)
{
// Throw an error
ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
// Return empty value
return scope.Close(Undefined());
}
// If one of the first two parameters is not a number
if(!args[0]->IsNumber() || !args[1]->IsNumber())
{
// Throw an error and return a null value
ThrowException(Exception::TypeError(String::New("Wrong arguments")));
return scope.Close(Undefined());
}
// For details, please refer to v8 documentation
// http://izs.me/v8-docs/classv8_1_1Value.html#a6eac2b07dced58f1761bbfd53bf0e366)
// `NumberValue` function
Local<Number> num = Number::New(args[0]->NumberValue() + args[1]->NumberValue());
return scope.Close(num);
}
The function is done!
Finally, write the export function at the end and it will be OK.
The code copy is as follows:
void Init(Handle<Object> exports)
{
exports->Set(String::NewSymbol("add"),
FunctionTemplate::New(Add)->GetFunction());
}
NODE_MODULE(addon, Init)
After you compile, we can use it like this:
The code copy is as follows: var addon = require('./build/Release/addon');
console.log(addon.add(1, 1) + "b");
You will see a 2b! ✧。٩(ᗜ)و✧*.
Callback function
In the previous chapter, we only talked about Hello world. In this chapter, the Grandma Master discovered it with conscience and wrote a callback function.
For convention, let’s write a framework first:
The code copy is as follows:
#include <node.h>
using namespace v8;
Handle<Value> RunCallback(const Arguments& args)
{
HandleScope scope;
// ... Cracky crackle crackle crackle crackle crackle crackle crackle crackle
return scope.Close(Undefined());
}
Then we decide how it is used like this:
func(function(msg) {
console.log(msg);
});
That is, it will pass a parameter to the callback function, we imagine it is a string, and then we can console.log() to see it.
First you need a string series
Without further ado, let's feed it a string first. (√ ζ ε:)
But we have to make this string of generic types because the Node.js code is weak.
Local<Value>::New(String::New("hello world"));
What? You ask me what is Local<Value>?
Then I will talk about it a little, refer to the reference documents from here and V8.
As the documentation shows, Local<T> actually inherits from Handle<T>, and I remember that the previous chapter had already talked about Handle<T>.
Then here is the Local.
There are two types of Handle, Local Handle and Persistent Handle, which are Local<T> : Handle<T> and Persistent<T> : Handle<T> . There is no difference between the former and Handle<T> and both the survival cycles are within scope. The latter's life cycle is separated from scope , you need to manually call Persistent::Dispose to end its life cycle . In other words, Local Handle is equivalent to allocating objects on the stack on C++` and Persistent Handle is equivalent to C++ allocating objects on the heap.
Then you need a parameter table series
How to get command line parameters after calling C/C++ on the terminal command line?
The code copy is as follows:
#include <stdio.h>
void main(int argc, char* argv[])
{
// ...
}
By the way, the argc here is the number of command line parameters, and argv[] is the various parameters. Then v8 also uses a similar method to call Node.js' callback function:
The code copy is as follows: V8EXPORT Local<Value> v8::Function::Call(Handle<Object>recv,
int argc,
Handle<Value> argv[]
);
~~QAQ is stuck in Handle<Object> recv! ! ! Keep writing tomorrow. ~~
Well, the new day begins and I feel like I'm full of strength. (∩^o^)⊃━☆゚.*・。。
After I have verified in many aspects (SegmentFault, StackOverflow and a QQ group), I finally solved the meaning of the three parameters of the above function.
I won’t talk about the last two parameters, one is the number of parameters, and the other is an array of parameters. As for the first parameter Handle<Object> recv, StackOverflow Brother’s explanation is as follows:
It is the same as applied in JS. In JS, you do
The code copy is as follows:
var context = ...;
cb.apply(context, [ ...args...]);
The object passed as the first argument becomes this within the function scope. More documentation on MDN. If you don't know JS well, you can read more about JS's this here: http://unschooled.org/2012/03/understanding-javascript-this/
― Excerpted from StackOverflow
In short, its function is to specify this pointer to the called function. The usage of this Call is similar to bind(), call(), and apply() in JavaScript.
So what we have to do is to build the parameter table first, and then pass in this Call function for its execution.
The first step is to display the conversion function, because it was originally an Object type:
Local<Function> cb = Local<Function>::Cast(args[0]);
The second step is to create a parameter table (array):
Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
The final call to the function series
Call cb and pass the parameters in:
cb->Call(Context::GetCurrent()->Global(), 1, argv);
The first parameter here Context::GetCurrent()->Global() means to obtain this function of the global context; the second parameter is the number in the parameter table (after all, although the array of Node.js has a length attribute, the system actually does not know the length of the array in C++, and you have to pass in a number to explain the length of the array); the last parameter is the parameter table we just established.
Final chapter end file series
I believe that everyone is already familiar with this step, just write the function, then put it into the export function, and finally declare it.
I'll just post the code, or just go to the Node.js document to read it.
The code copy is as follows:
#include <node.h>
using namespace v8;
Handle<Value> RunCallback(const Arguments& args)
{
HandleScope scope;
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
cb->Call(Context::GetCurrent()->Global(), argc, argv);
return scope.Close(Undefined());
}
void Init(Handle<Object> exports, Handle<Object> module)
{
module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(RunCallback)->GetFunction());
}
NODE_MODULE(addon, Init)
Well done! Let's go the last steps yourself. As for calling this function in Js, I have mentioned it before.
Extra
Well, I feel that my study notes are becoming more and more unrestrained and want to break them~
Let’s stop here today. During the process of writing study notes, I have increased my posture again, such as the parameter meaning of the Call function.
If you think this series of study notes is helpful to you, come and have a good time with me~Σ>(〃°ω°〃)♡→