node c++ 调用第三方库之TinyXPath
发布于 9 年前 作者 vincent-zheng 4952 次浏览 最后一次编辑是 8 年前 来自 分享

node c++ 调用第三方库之TinyXPath

本程序使用c++的第三方库TinyXPath进行实验,以探究c++调用第三方库的方法,TinyXPath官网可以点击这里查看。 原文链接 https://github.com/vincent-zheng/blog/blob/master/201506/1.md

简述

本程序主要参照nodejs官方文档和部分v8引擎源码(这里我也正在看,所以可能会有比较多的误差)。程序完整源码可以点击这里获取。

编译环境

本程序在VS2013和gcc4.9.2下编译通过,nodejs版本为0.12.4。

完整源码解析

main.cxx

程序主入口文件, 用以初始化类,本程序使用nodejs里面的WrapObject的方式来实现用c++代码构建js对象。下面只是调用XPath这个类里面的Init函数初始化对象,所以没什么好说的了 ~~

// main.cxx
#include "xpath4js.h"
using namespace v8;
using namespace std;

void init(Handle<Object> exports) {
	XPath::Init(exports);
}

NODE_MODULE(xpath4js, init)

xpath4js.h

XPath对象头文件,定义的Init函数用以初始化对象。调用TinyXPath这个第三方库只需要包含xpath_static.h这个头文件即可,TinyXPath官方文档上面是说明TinyXPath这个库是先通过编译成静态库然后调用的,而我这里为了方便同时支持windows和linux平台,所以将这个编译的方式改成了直接编译源文件,在后面的binding.gyp文件中会有说明。需要另外说明的是对象中需要包含在exports出去的js对象中的话需要使用静态函数,当然,需要调用到的变量不需要这样做,不过记得要在构造函数中构造好。

#ifndef XPATH4JS
#define XPATH4JS

#include "../lib/tinyxpath_1_3_1/xpath_static.h"
#include <node.h>
#include <node_object_wrap.h>
#include <iostream>

class XPath : public node::ObjectWrap {
public:
	static void Init(v8::Handle<v8::Object> exports);
private:
	explicit XPath(const char * xml);
	~XPath();

	static void New(const v8::FunctionCallbackInfo<v8::Value> & args);
	static void parse(const v8::FunctionCallbackInfo<v8::Value> & args);
	static void get(const v8::FunctionCallbackInfo<v8::Value> & args);
	static v8::Persistent<v8::Function> constructor;

	char * _xml;
	TiXmlDocument * doc;
};

#endif 

xpath4js.cxx

为了方便说明,这个文件的说明直接写在相应代码行的注释中。

#include "xpath4js.h"
using namespace v8;
using namespace std;

Persistent<Function> XPath::constructor;

// 用以获取char *字符串的长度
int charLength(const char * str) {
	int i = 0;
	while (str[i] != '\0') {
		i++;
	}
	return i;
}

// 构造函数, 配置相应的非静态变量
XPath::XPath(const char * xml): _xml(xml) {
	doc = new TiXmlDocument;
	if (charLength(xml)) {
		doc->Parse(xml);
	}
}

XPath::~XPath() {

}

// xports的初始化方法, 将c++对象转化为js对象
void XPath::Init(Handle<Object> exports) {
    // 当然是获取当前的Isolate句柄先
	Isolate * isolate = Isolate::GetCurrent();

    /* JS中的对象就是使用函数的方式实现的,而c++这里也是一样, 
       使用FunctionTemplate这个对象构造出相应的函数,然后对
       他的原型链进行相应的函数赋值,这里的函数是调用XPath这
       个类里面的New函数作为构造函数的。
    */
	Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
	tpl->SetClassName(String::NewFromUtf8(isolate, "XPath"));
	tpl->InstanceTemplate()->SetInternalFieldCount(1);

    // 将函数插入原型链中
	NODE_SET_PROTOTYPE_METHOD(tpl, "parse", parse);
	NODE_SET_PROTOTYPE_METHOD(tpl, "get", get);
	constructor.Reset(isolate, tpl->GetFunction());
    // exports出对象
	exports->Set(String::NewFromUtf8(isolate, "XPath"), tpl->GetFunction());
}

// 对象的构造方法
void XPath::New(const FunctionCallbackInfo<Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	XPath * instance;
    // 这里不是很确定,是模仿nodejs官档上面的做法的,我猜测是判断在js调用的时候是否为使用new构造。
	if (args.IsConstructCall()) {
		// 我原以为会有IsString方法,但是查看了一下v8里面的源码,发现是没有的~~ , 我猜测是因为js中所有对象都是可以变成String类型的,所以不需要强加判断
		if (args.Length() > 0 /* && args[0].IsString()*/) {
            // 获取参数并转化为const char * 类型
			String::Utf8Value str(args[0]);
			const char * xml = * str;
            // 实例化XPath对象
			instance = new XPath(xml);
		} else {
			instance = new XPath("");
		}
        // 将对象传递出去
		instance->Wrap(args.This());
		args.GetReturnValue().Set(args.This());
	} else {
        // 这里面是如果不是使用new 构造就将其内部转化成使用new构造时的方式。
		Local<Function> func = Local<Function>::New(isolate, constructor);
		if (args.Length() > 0 /* && args[0].IsString()*/) {
			String::Utf8Value str(args[0]);
			const char * xml = * str;
			Local<Value> argv[1] = {String::NewFromUtf8(isolate, xml)};
			args.GetReturnValue().Set(func->NewInstance(1, argv));
		} else {
			Local<Value> argv[1] = {String::NewFromUtf8(isolate, "")};
			args.GetReturnValue().Set(func->NewInstance(1, argv));
		}
	}
}

// 定义了一个parse方法,用于传入需要用XPath解析的文档
void XPath::parse(const v8::FunctionCallbackInfo<v8::Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);

	XPath * instance = ObjectWrap::Unwrap<XPath> (args.Holder());
	if (args.Length() > 0  /* && args[0].IsString()*/) {
		String::Utf8Value str(args[0]);
		const char * xml = *str;
		instance->_xml = xml;
		instance->doc->Parse(instance->_xml);
		args.GetReturnValue().Set(Boolean::New(isolate, true));
	} else {
		args.GetReturnValue().Set(Boolean::New(isolate, false));
	}
}

// 使用get方法获取XPath路径下的内容。
void XPath::get(const v8::FunctionCallbackInfo<v8::Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);

	XPath * instance = ObjectWrap::Unwrap<XPath>(args.Holder());
	if (args.Length() > 0) {
		String::Utf8Value str(args[0]);
		const char * path = *str;
		TIXML_STRING S_res = TinyXPath::S_xpath_string(instance->doc->RootElement(), path);
		Local<String> result = String::NewFromUtf8(isolate, S_res.c_str());
		args.GetReturnValue().Set(result);
	} else {
		args.GetReturnValue().Set(String::NewFromUtf8(isolate, ""));
	}
}

test.js

测试代码

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

var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
           	"<root>" +
                "<child foo='bar'>" +
                    "<grandchild baz=\"fizbuzz\">grandchild content</grandchild>" +
                "</child>" +
                "<sibling>with content!</sibling>" + 
           	"</root>";

var xpath = new xpath4js.XPath();
xpath.parse(xml);
console.log(xpath.get('//grandchild/text()'));

回到顶部