如何使用范型技术在C++中添加对JavaScript的支持
来源:互联网 发布:软件开发的发展前景 编辑:程序博客网 时间:2024/06/05 13:18
前段时间在做M2M产品开发过程中需要加入对JavaScript的支持,以便使很多逻辑可以下载到嵌入式网关中执行。我比较了现在三种主要的JavaScript引擎,分别是: SpiderMonkey,MS JS,V8,最后选择使用SpiderMonkey做为项目中使用的引擎。关于三种引擎的比较我以后会写出来。
在使用SpiderMonkey中发现要把现在有的C++类和函数导入jscontext是件很麻烦的事,你必须把可变类型的jsval转换成有明确类型的C++变量传递给相应的被调用函数。这个工作需要你大量地编写差不多的代码。可以想像如果你要把50个类,平均每个类有10个方法,而每个方法又有5个参数,那这个工作量可能是你的梦魇。而且你辛苦写的代码可能有很多bug,对测试工作也是一个极大的考验。并且在SpiderMonkey中并不支持C++的异常机制,如果你在C++的函数中产生异常并没有处理那么就会导致整个程序崩溃。于是我考虑使用boost提供的meta-programming机制来实现类型的自动转换。
下面我贴出部分代码供大家分享:
template<typename FuncT, class From = typename mpl::begin<ft::parameter_types<FuncT>>::type,
class To = typename mpl::end<ft::parameter_types<FuncT>>::type>
struct CPPInvoker;
template<typename FuncT, class From, class To>
struct CPPInvoker
{
// add an argument to a Fusion cons-list for each parameter type
template<typename FusionArgsT>
static inline void apply(FuncT func, JSContext* cx, jsval* argv, FusionArgsT const& fuArgs, jsval& rval)
{
typedef typename mpl::deref<From>::type arg_type;
typedef typename mpl::next<From>::type next_iter_type;
CPPInvoker<FuncT, next_iter_type, To>::apply(func, cx, argv + 1, fusion::push_back(fuArgs, jsval_to<arg_type>(cx, *argv)), rval);
}
};
template<typename FuncT, class To>
struct CPPInvoker<FuncT, To, To>
{
// the argument list is complete, now call the function
template<typename FusionArgsT>
static inline void apply(FuncT func, JSContext* cx, jsval* argv, FusionArgsT const& fuArgs, jsval& rval)
{
applyRetTIsVoid(func, cx, argv, fuArgs, rval, is_void<ft::result_type<FuncT>::type>());
}
template<typename FusionArgsT>
static inline void applyRetTIsVoid(FuncT func, JSContext* cx, jsval* argv, FusionArgsT const& fuArgs, jsval& rval, const false_type&)
{
rval = to_jsval(cx, fusion::invoke(func, fuArgs));
}
template<typename FusionArgsT>
static inline void applyRetTIsVoid(FuncT func, JSContext* cx, jsval* argv, FusionArgsT const& fuArgs, jsval& rval, const true_type&)
{
fusion::invoke(func, fuArgs);
}
};
class CDefineFuncBase
{
public:
JSFunctionSpec spec;
};
template<typename FuncT, int _NO = 0>
class CDefineFunc : public CDefineFuncBase
{
public:
CDefineFunc(const char* pszFuncName, FuncT func)
{
s_func = func;
spec.name = pszFuncName;
spec.flags = 0;
spec.call = &call_func<is_member_function_pointer<FuncT>::type>;
spec.extra = 0;
// Get count of parameters to register function in SpiderMonkey
spec.nargs = ft::function_arity<FuncT>::value - 1;
}
private:
static FuncT s_func;
template<typename IsMemberT>
static JSBool call_func(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
typedef typename mpl::begin<ft::parameter_types<FuncT>>::type BeginT;
typedef typename remove_reference<mpl::deref<BeginT>::type>::type ClassT;
SMW_JS_TRY
CPPInvoker<FuncT, mpl::next<BeginT>::type>::apply(s_func, cx, argv, fusion::push_back(fusion::nil(), JSObjectConvert<ClassT*>(cx, obj)), *rval);
SMW_JS_CATCH(cx)
}
template<>
static JSBool call_func<false_type>(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
typedef typename mpl::begin<ft::parameter_types<FuncT>>::type BeginT;
SMW_JS_TRY
CPPInvoker<FuncT, mpl::next<BeginT>::type>::apply(s_func, cx, argv, fusion::push_back(fusion::nil(), cx), *rval);
SMW_JS_CATCH(cx)
}
};
template<typename FuncT, int _NO> FuncT CDefineFunc<FuncT, _NO>::s_func;
template<class ClassT>
class CJSObject : public CJSObjectBase
{
public:
CJSObject(JSContext* cx, JSObject *obj) : CJSObjectBase(cx, obj) {}
// Registers a function with the interpreter.
template<int _NO, typename FuncT>
static typename enable_if<ft::is_member_function_pointer<FuncT>>::type DefineMember(const char* pszFunctionName, FuncT func)
{
for (unsigned i = 0; i < ms_Members.size(); ++i)
if (std::string(ms_Members[i]->spec.name) == pszFunctionName) return;
ms_Members.push_back(static_cast<CDefineFuncBase*>(new CDefineFunc<FuncT, _NO>(pszFunctionName, func)));
}
static ClassT* This() { return (ClassT*)0; }
template<class PropertyT>
static void DefineProperty(const char* pszPropertyName, PropertyT& _property, unsigned _attributes = JSPROP_ENUMERATE)
{
for (int i = 0; i < ms_PropertySpecCount; ++i)
if (std::string(ms_PropertySpecs[i].name) == pszPropertyName) return;
ms_PropertySpecs[ms_PropertySpecCount].name = pszPropertyName;
ms_PropertySpecs[ms_PropertySpecCount].tinyid = ms_PropertySpecCount;
ms_PropertySpecs[ms_PropertySpecCount].flags = _attributes;
ms_GetPropertyFunctions.push_back(boost::bind(&_getProperty<PropertyT>, _1, _2, (int)&_property));
ms_SetPropertyFunctions.push_back(boost::bind(&_setProperty<PropertyT>, _1, _2, (int)&_property, _3));
++ms_PropertySpecCount;
}
static void Register(JSContext* cx, const char* pszClassName, JSObject* obj = NULL)
{
JSFunctionSpec ClassMethodSpecs[MAX_CLASS_METHOD_SPECS];
ms_JSClass.name = pszClassName;
memset(&ClassMethodSpecs, 0, sizeof(ClassMethodSpecs));
for (unsigned i = 0; i < ms_Members.size(); ++i)
ClassMethodSpecs[i] = ms_Members[i]->spec;
/* Mandatory non-null function pointer members. */
ms_JSClass.addProperty = JS_PropertyStub;
ms_JSClass.delProperty = JS_PropertyStub;
ms_JSClass.getProperty = JS_getPropertyStub;
ms_JSClass.setProperty = JS_setPropertyStub;
ms_JSClass.enumerate = JS_EnumerateStub;
ms_JSClass.resolve = JS_ResolveStub;
ms_JSClass.convert = JS_ConvertStub;
ms_JSClass.construct = Construct;
ms_JSClass.finalize = Destory;
ms_JSClass.flags = JSCLASS_HAS_PRIVATE;
if (cx)
SMW_SAFE_CALL(cx, ms_JSObject = JS_InitClass(cx, obj ? obj : JS_GetGlobalObject(cx), NULL, &ms_JSClass,
Construct, 0, ms_PropertySpecs, ClassMethodSpecs, NULL, NULL));
}
virtual jsval RawGetObjProperty(const WString& strName)
{
ProperiesT::iterator pProperty = m_Properties.find(strName);
if (pProperty != m_Properties.end())
{
if (pProperty->second.Type == ptMember)
return ms_GetPropertyFunctions[pProperty->second.Value](m_cx, this);
else if (pProperty->second.Type == ptJSVal)
return pProperty->second.Value;
return JSVAL_VOID;
}
else
throw std::exception((string("CJSObjectBase::GetObjProperty() Bad property name ") + WStrToMBS(strName)).c_str());
}
virtual void RawSetObjProperty(const WString& strName, jsval Value)
{
ProperiesT::iterator pProperty = m_Properties.find(strName);
if (pProperty != m_Properties.end())
{
if (pProperty->second.Type == ptJSVal)
pProperty->second.Value = Value;
else if (pProperty->second.Type == ptMember)
{
if (!(ms_PropertySpecs[pProperty->second.Value].flags & JSPROP_READONLY))
ms_SetPropertyFunctions[pProperty->second.Value](m_cx, this, Value);
}
}
else if (m_bAllowAddProperty)
DefineObjProperty(strName, _jsval(Value), JSPROP_ENUMERATE);
else
throw std::exception((string("CJSObjectBase::SetObjProperty() Cannot add property name ") + WStrToMBS(strName)).c_str());
}
static JSClass ms_JSClass;
static JSObject* ms_JSObject;
private:
static int ms_PropertySpecCount;
static JSPropertySpec ms_PropertySpecs[MAX_PROPERTY_SPECS];
static std::vector<CDefineFuncBase*> ms_Members; // stores all members of class called by SpiderMonkey
static std::vector<function<jsval (JSContext*, void*)>> ms_GetPropertyFunctions;
static std::vector<function<void (JSContext*, void*, jsval)>> ms_SetPropertyFunctions;
static JSBool JS_getPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
SMW_JS_TRY
if (JSVAL_IS_STRING(id))
{
jsval jsRet = JSObjectConvert<ClassT*>(cx, obj)->RawGetObjProperty(jsval_to<WString>(cx, id));
if (jsRet != JSVAL_VOID) *vp = jsRet;
return JSVAL_TRUE;
}
if (!JSVAL_IS_INT(id) || (JSVAL_TO_INT(id) >= (int)ms_GetPropertyFunctions.size()))
throw std::exception("CJSObject::GetPropertyIndexByID() Bad property index!");
*vp = ms_GetPropertyFunctions[JSVAL_TO_INT(id)](cx, JSObjectConvert<ClassT*>(cx, obj));
SMW_JS_CATCH(cx)
}
static JSBool JS_setPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
SMW_JS_TRY
if (JSVAL_IS_STRING(id))
{ JSObjectConvert<ClassT*>(cx, obj)->RawSetObjProperty(jsval_to<WString>(cx, id), *vp); return JSVAL_TRUE; }
if (!JSVAL_IS_INT(id) || (JSVAL_TO_INT(id) >= (int)ms_GetPropertyFunctions.size()))
throw std::exception("CJSObject::JS_setPropertyStub() Bad property index!");
if (!(ms_PropertySpecs[JSVAL_TO_INT(id)].flags & JSPROP_READONLY))
ms_SetPropertyFunctions[JSVAL_TO_INT(id)](cx, JSObjectConvert<ClassT*>(cx, obj), *vp);
SMW_JS_CATCH(cx)
}
template <class PropertyT>
static jsval _getProperty(JSContext *cx, void* _this, int _Offset)
{
return to_jsval(cx, *(PropertyT*)((char*)_this + _Offset));
}
template <class PropertyT>
static void _setProperty(JSContext *cx, void* _this, int _Offset, jsval _Value)
{
*(PropertyT*)((char*)_this + _Offset) = jsval_to<PropertyT>(cx, _Value);
}
static JSBool Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
SMW_JS_TRY
if (!JS_IsConstructing(cx))
throw std::exception("CJSObject::Construct() Construct may not be called as a function!");
ClassT* newObj = new ClassT(cx, obj, argc, argv, rval);
*rval = OBJECT_TO_JSVAL(obj);
for (int i = 0; i < ms_PropertySpecCount; ++i)
newObj->m_Properties[ms_PropertySpecs[i].name] = SProperty(ptMember, i);
for (unsigned i = 0; i < ms_Members.size(); ++i)
newObj->m_Properties[ms_Members[i]->spec.name] = SProperty(ptFunction, JS_TRUE);
SMW_JS_CATCH(cx)
}
static void Destory(JSContext *cx, JSObject *obj)
{
try {
delete JSObjectConvert<ClassT*>(cx, obj);
} catch(...) {}
}
};
template<class ClassT> int CJSObject<ClassT>::ms_PropertySpecCount;
template<class ClassT> JSPropertySpec CJSObject<ClassT>::ms_PropertySpecs[MAX_PROPERTY_SPECS];
template<class ClassT> std::vector<CDefineFuncBase*> CJSObject<ClassT>::ms_Members;
template<class ClassT> std::vector<function<jsval (JSContext*, void*)>> CJSObject<ClassT>::ms_GetPropertyFunctions;
template<class ClassT> std::vector<function<void (JSContext*, void*, jsval)>> CJSObject<ClassT>::ms_SetPropertyFunctions;
template<class ClassT> JSClass CJSObject<ClassT>::ms_JSClass;
template<class ClassT> JSObject* CJSObject<ClassT>::ms_JSObject;
template<typename FuncT, class From = typename mpl::next<mpl::begin<ft::parameter_types<FuncT>>::type>::type,
class To = typename mpl::end<ft::parameter_types<FuncT>>::type>
struct JSInvoker;
template<typename FuncT, class From, class To>
struct JSInvoker
{
// add an argument to a Fusion cons-list for each parameter type
static inline jsval apply(JSContext* cx, const char* pszFuncName, void* pParams, jsval* argv1, jsval* argv2)
{
typedef typename mpl::deref<From>::type arg_type;
typedef typename mpl::next<From>::type next_iter_type;
arg_type* pParam = (arg_type*)(pParams);
*argv1 = to_jsval(cx, *pParam);
return JSInvoker<FuncT, next_iter_type, To>::apply(cx, pszFuncName, pParam + 1, argv1 + 1, argv2);
}
};
template<typename FuncT, class To>
struct JSInvoker<FuncT, To, To>
{
// the argument list is complete, now call the function
static inline jsval apply(JSContext* cx, const char* pszFuncName, void* pParams, jsval* argv1, jsval* argv2)
{
jsval jsRet;
SMW_CHECK_CALL(cx, JS_CallFunctionName(cx, JS_GetGlobalObject(cx), pszFuncName, ft::function_arity<FuncT>::value - 1, argv2, &jsRet),
(string("JSInvoker() call function ") + pszFuncName + " failed!").c_str());
return jsRet;
}
};
template<typename FuncT, int _NO = 0>
struct CJavascriptFunc
{
static const char* s_pszFuncName;
static FuncT inline GetStub(const char* pszFuncName)
{
s_pszFuncName = pszFuncName;
return reinterpret_cast<FuncT>(Stub<ft::result_type<FuncT>::type>);
}
template<typename RetT>
static RetT inline Stub(JSContext* cx, int First)
{
jsval Params[ft::function_arity<FuncT>::value];
jsval jsret = JSInvoker<FuncT>::apply(cx, s_pszFuncName, &First, Params, Params);
return jsval_to<RetT>(cx, jsret);
}
template<>
static void inline Stub<void>(JSContext* cx, int First)
{
jsval Params[ft::function_arity<FuncT>::value];
JSInvoker<FuncT>::apply(cx, s_pszFuncName, &First, Params, Params);
}
};
template<typename FuncT, int _NO> const char* CJavascriptFunc<FuncT, _NO>::s_pszFuncName;
template<class JSClassT>
JSClassT inline JSObjectConvert(JSContext *cx, JSObject* obj)
{
JSClassT retObj = (obj ? (JSClassT)JS_GetPrivate(cx, obj) : NULL);
if (!retObj) throw std::exception("JSObjectConvert() retObj is NULL!");
return retObj;
}
template<class JSClassT>
JSClassT* CreateJSObject(JSContext *cx, int iParamsCount = 0, ...)
{
va_list Params;
JSObject* retObj;
va_start(Params, iParamsCount);
SMW_SAFE_CALL(cx, retObj = (iParamsCount == 0) ? JS_ConstructObject(cx, &JSClassT::ms_JSClass, NULL, NULL)
: JS_ConstructObjectWithArguments(cx, &JSClassT::ms_JSClass, NULL, NULL, iParamsCount, (jsval*)Params));
return JSObjectConvert<JSClassT*>(cx, retObj);
}
使用时只需要CJSObject定义类的方法和属性就可以导入到JSContext中,测试代码如下:
class MyT : public SMW::CJSObject<MyT>
{
public:
// Required ctor:
MyT( JSContext * cx, JSObject *obj, uintN argc,
jsval * argv, jsval * rval ) : CJSObject<MyT>(cx, obj) { id = 5; name = "abcd"; }
// member functions we want to bind:
int funcA() {
return 123; }
double funcB(int a, int b) {
return 3.14; }
int funcC(MyT* a, int b) {
return a->id + b;
}
// these strings are explained below:
int id;
std::string name;
static void Register(JSContext* cx)
{
DefineMember<0>("funcA", &funcA);
DefineMember<0>("funcB", &funcB);
DefineMember<0>("funcC", &funcC);
DefineProperty("id", This()->id);
DefineProperty("name", This()->name);
SMW::CJSObject<MyT>::Register(cx, "MyT");
}
};
调用类MyT中的Register注册到指定JSContext中后,就可以使用JS直接建立相应对象实例使用了。
以上代码在VC9中编译通过并运行正确。
- 如何使用范型技术在C++中添加对JavaScript的支持
- VS2010如何在控制台应用程序中添加对MFC库的支持
- 在Windows CE 6.0中添加对MFC的支持
- 在Windows CE 6.0中添加对MFC的支持
- 在Android4.2.2中添加对exFAT文件系统的支持
- 在Android4.2.2中添加对exFAT文件系统的支持
- 在vim配置文件中添加对Python的支持
- 在Source Insight中添加对.cc的支持
- 如何在程序中加入对vbscript的支持
- 如何使用Eclipse中对Web Browser的支持
- 在ecshop中如何实现对一个模板的添加?
- 对在Qtopia中添加国际化支持一文补充
- 在Oracle sql developer1.54中添加对mysql和sql server2000的数据库连接支持
- 在Android系统中添加对新遥控器按键的支持
- 在Android系统中添加对新遥控器按键的支持
- 在Android系统中添加对新遥控器按键的支持
- 在Android系统中添加对新遥控器按键的支持
- 在YouCompleteMe+Syntastic中添加和取消对C++11的支持
- xp 中支持php的环境的iis配置
- sql*plus命令 之 文件操作命令
- 参考SlidingDrawer的Android animation 的算法
- POJ 1256 Anagram
- VC如何打开一个固定大小的视图
- 如何使用范型技术在C++中添加对JavaScript的支持
- asp.net的总结(转帖)
- 理解Service
- 一次URL请求后台响应两次
- qq临时对话代码,MSN、淘宝旺旺、Skype在线对话代码
- 做一下mssql的连接,和赋值的测试
- 曾经一份资源在我面前,我没有好好珍惜(原来是没有积分)
- watchpoint和breakpoint
- sql*plus命令 之 数据库操作命令