javascript 调用flash-GoogleV8编程解读(五)JS调用C++

2023-09-01 0 9,944 百度已收录

最近因为忙于解决个人独身问题,所以已经很久没有更新第五章了。

上一章主要讲了GoogleV8的Context概念。 所以很明显,GoogleV8的基本概念包括FunctionTemplate、ObjectTemplate等重要的基本概念,这些都会在后续章节中渗透。

本章主要讲如何通过V8实现JS调用C++。 JS调用C++,分为JS调用C++函数(全局)、调用C++类。

JS调用C++函数

JS调用C++函数,通过FunctionTemplate和ObjectTemplate进行扩展。

FunctionTemplate、ObjectTemplate可以理解为JSfunction和C++函数的绑定。 FunctionTemplate实现了JS函数和C++函数的绑定。 事实上,这些绑定是双向的,只能实现JS调用C++函数。 说得更直白一点javascript 调用flash,FunctionTemplate和ObjectTemplate就相当于JS的函数和对象。

基本原理是先通过FunctionTemplate绑定C++函数,然后将FunctionTemplate注册到JS全局,这样JS就可以调用C++函数了。

代码如下所示:

上一段代码实现了在JS中调用C++Yell()函数。

基本步骤分为三个步骤:A、B、C:

#include "v8.h"
#include 
#include 
using namespace v8;
using namespace std;
Handle Yell(const Arguments& args) {
	HandleScope  handle_scope;
	char buffer[4096];
	
	memset(buffer, 0, sizeof(buffer));
	Handle str = args[0]->ToString();
	str->WriteAscii(buffer);
	printf("Yell: %sn", buffer);
	return Undefined();
}
int main(int argc, char** argv) {
	HandleScope handle_scope;
	//A
	Handle fun = FunctionTemplate::New(Yell);
	//B
	Handle global = ObjectTemplate::New();
	global->Set(String::New("yell"), fun);
	//C
	Persistent cxt = Context::New(NULL, global);
	Context::Scope context_scope(cxt);
	Handle source = String::New("yell('Google V8!')");
	Handle script = Script::Compile(source);
	Handle result = script->Run();
	cxt.Dispose();
}

第一步是定义一个 FunctionTempte 并将其绑定到 C++ 函数:

Handle fun = FunctionTemplate::New(Yell);

第二部分定义一个ObectTemplate并向该对象注册一个FunctionTemplate

	Handle global = ObjectTemplate::New();
	global->Set(String::New("yell"), fun);

第三步,在JS的全局中注册对象:

Persistent cxt = Context::New(NULL, global);

JS调用C++类

JS 似乎很难直接使用 C++ 类。 当JS中new一个对象时,需要自动将C++形成的对象绑定到JS对象上。 这导致了使用C++类的JS的出现:

var cloudapp = new CloudApp();
cloudapp.xxInterface();

V8在这方面还不够强大javascript 调用flash,而Qt的QML(类JS脚本语言)可以实现手动绑定。

内部场

当JSnew一个对象时,C++也会同步一个新对象并在C++内部保存指针,并维护指针列表,这就是V8InternalField的作用。 所有需要与JS绑定的C++指针都存在于这个InternalField中。 尽管它只是一个列表,但 V8Object 可以具有任意数量的 InternalField。 如果需要使用InterField中存储的C++指针,直接获取即可:

将C++表针封装到InternalField中:

//....
void* ptr = ...
object->SetInternalField(0, External::New(ptr));

前面的代码将 C++ 指针 ptr 保存在 InternalField 的索引 0 处。 如果将来需要获取这个指针,只需使用index0来获取指针即可。

从InternalField获取C++监视指针:

Local wrap = Local::Cast(object->GetInternalField(0));
void* ptr = wrap->Value();

object->GetInternalField(0) 是从InternalField 中检索index=0 处的C++ 指针。

外部的

既然说到了C++指针的绑定,就必须要说一下V8的External。 V8的External专门用于包裹(Wrap)和展开(UnWrap)C++手表引脚。 V8的外部实现如下:

Local External::Wrap(void* value) {
  return External::New(value);
}
void* External::Unwrap(Handle obj) {
  return External::Cast(*obj)->Value();
}

javascript 调用flash-GoogleV8编程解读(五)JS调用C++

外部似乎是C++手中的载体。 这也解释了上面在InternalField中设置和获取C++指针时使用External::New和wrap->Value()的动机。 external::Value() 返回 C++ 指针。

我们先从代码开始,看看JS是如何调用C++类的:

//C++Externtion
#include "v8.h"
#include "utils.h"
#include 
#include 
using namespace std;
using namespace v8;
enum AppState{
	IDEL = 0,
	LOADED,
	STOP
};
class CloudApp {
public:
	CloudApp(int id) { 
		state = IDEL;
		appId = id;
	}
	void start() {
		cout << "CloudApp been Loaded id = " << appId << endl;
		state = LOADED;
	};
	int getState() { return state;}
	int getAppId() { return appId;}
	
private:
	AppState state;
	int appId;	
};
//向MakeWeak注册的callback.
void CloudAppWeakReferenceCallback(Persistent object
												, void * param) {
	if (CloudApp* cloudapp = static_cast(param)) {
		delete cloudapp;
	}
}
//将C++指针通过External保存为Persistent对象,避免的指针被析构
Handle MakeWeakCloudApp(void* parameter) {
	Persistent persistentCloudApp = 
		Persistent::New(External::New(parameter));
		
//MakeWeak非常重要,当JS世界new一个CloudApp对象之后
//C++也必须new一个对应的指针。
//JS对象析构之后必须想办法去析构C++的指针,可以通过MakeWeak来实现,
//MakeWeak的主要目的是为了检测Persistent Handle除了当前Persistent 
//的唯一引用外,没有其他的引用,就可以析构这个Persistent Handle了,
//同时调用MakeWeak的callback。这是我们可以再这个callback中delete 
//C++指针
	persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback);
	return persistentCloudApp;
}
//将JS传进来的参数解析之后,创建C++对象
CloudApp* NewCloudApp(const Arguments& args) {
	CloudApp* cloudApp = NULL;
	
	if (args.Length() == 1) {
		cloudApp = new CloudApp(args[0]->ToInt32()->Value());	
	} else {
		v8::ThrowException(String::New("Too many parameters for NewCloudApp"));
	}
	return cloudApp;
}
//相当于JS对应的构造函数,当JS中使用new CloudApp的时候,这个callback将自动被调用
Handle CloudAppConstructCallback(const Arguments& args) {
	if (!args.IsConstructCall())
		return Undefined();
	
	CloudApp* cloudapp = NewCloudApp(args);
	Handle object = args.This();
	object->SetInternalField(0, MakeWeakCloudApp(cloudapp));
	return Undefined();
}
Handle GetState(const Arguments& args) {
	Handle self = args.Holder();
	Local wrap = Local::Cast(self->GetInternalField(0));
	void* ptr = wrap->Value();
	CloudApp* cloudapp = static_cast(ptr);
	return Integer::New(cloudapp->getState());
}
Handle GetAppId(const Arguments& args) {
	Handle self = args.Holder();
	Local wrap = Local::Cast(self->GetInternalField(0));
	void* ptr = wrap->Value();
	CloudApp* cloudapp = static_cast(ptr);
	return Integer::New(cloudapp->getAppId());
} 
Handle Start(const Arguments& args) {
	Handle self = args.Holder();
	Local wrap = Local::Cast(self->GetInternalField(0));
	void* ptr = wrap->Value();
	CloudApp* cloudapp = static_cast(ptr);
	cloudapp->start();
	return Undefined();
}
void SetupCloudAppInterface(Handle global) {
	Handle cloudapp_template = 
		FunctionTemplate::New(CloudAppConstructCallback);
	cloudapp_template->SetClassName(String::New("CloudApp"));
	Handle cloudapp_proto = cloudapp_template->PrototypeTemplate();
	//这一步,完全可以使用cloudapp_inst->Set(....)
	//使用prototype更符合JS编程
	cloudapp_proto->Set(String::New("start"), FunctionTemplate::New(Start));
	cloudapp_proto->Set(String::New("state"), FunctionTemplate::New(GetState));
	cloudapp_proto->Set(String::New("appid"), FunctionTemplate::New(GetAppId));
	
	//******很重要!!!
	Handle cloudapp_inst = cloudapp_template->InstanceTemplate();
	cloudapp_inst->SetInternalFieldCount(1);
	
	//向JS世界注册一个函数,其本质就是向JS世界的global注册一个类。
	//所以,也是通过向global注入CloudApp类。
	global->Set(String::New("CloudApp"), cloudapp_template);
}
void InitialnilizeInterface(Handle global) {
	SetupCloudAppInterface(global);
}
void LoadJsAndRun() {
	Handle source = ReadJS("script.js");
	Handle script = Script::Compile(source);
	Handle result = script->Run();
	printValue(result);
}
void Regist2JsContext(Handle& object
							, Persistent& context) {
	context = Context::New(NULL, object);
}
int main(int argc, char** argv) {
	HandleScope handle_scope;
	Handle global = ObjectTemplate::New();
	Persistent context;
	
	InitialnilizeInterface(global);
	Regist2JsContext(global, context);
	Context::Scope context_scope(context);
	LoadJsAndRun();
	context.Dispose();
	
	return 0;
}

JS代码如下:

//script.js
var cloudapp = new CloudApp(24);
cloudapp.start();
var result;

里面的代码基本上从函数名和注释就可以明白它的意思。 最后说一下SetInternalFieldCount:

	Handle cloudapp_inst = cloudapp_template->InstanceTemplate();
	cloudapp_inst->SetInternalFieldCount(1);

当其他操作准备好后,需要SetInsternalFieldCount()。 这是告诉V8我们有几个InternalField,但这里只有一个。 否则,JS和C++表针交互过程中,V8搜索InternalField时会出界。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 javascript javascript 调用flash-GoogleV8编程解读(五)JS调用C++ https://www.wkzy.net/game/187407.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务