zoukankan      html  css  js  c++  java
  • UCScript——C++集成脚本

    使用UCScript作脚本,主要是因为它几个特点很适合用于集成(脚本引擎体积小,占用资源少,运行快,跨平台跨语言,与宿主语言交互方便),而且它的语法类C语言,不用去多学一门语言。在C++中集成UCScript脚本是轻而易举的事。我们接下来用实例来说明,如何把UCScript脚本集成到你的程序中去。

    1. 创建UCScript引擎

        ucScript ucs; 或者 ucScript* ucs= new ucScript;

    创建一个ucScript对象,就是创建一个UCScript脚本引擎。可以创建任意多个脚本引擎,甚至可以分布在不同的线程当中。

    注意:指定合适的预定义,Windows用_UC_WIN,Linux用_UC_LINUX(即编译参数-D_UC_LINUX)。如果以动态库的形式使用脚本,还得指定_UC_IMPORT。

    2. 执行脚本程序 你可以从缓冲区中装载脚本,也可以直接解析文件:

        const char * szCode="ret 1+2*3;";
        ucScript ucs;
        ucCalcVar vRet= ucs.Parse(szCode);
        assert(vRet.value.iVal == 7);

    如果你想对你的源代码进行保护而不是赤裸裸的袒露在大家的眼前,或者提高脚本性能,你可以把脚本编译成二进制中间码。

    3. 如何让脚本调用C++的函数。比如让脚本以string: get_peername(int: id)的形式调用我的C++代码,代码可以写成:

    UCSFUNC3(get_peername,CVT_STRING, "get peer name by peer id",
            CVT_INTEGER,"puid",
            CVT_STRING,"def=/"unknown/"",
            1)
    {
        int puid;
        //  get an integer value from the first parameter
        ucAdaptCVT::cvt2out(params[0], puid);

        CPeer * p=CPeerMgr::Instance().GetPeer(puid);
        if(p) {
            return p->m_PeerInfo.name;  //  return the string of name
        }
        const char * def="unknown";
        if(count>1) {
            ucAdaptCVT::cvt2out(params[1], def);  //  get default name specified by parameter
        }
        return def;
    }

    上面代码用了UCSFUNC(xxx) 辅助宏,定义在ucScriptFunc.h头文件。其中xxx表示脚本变量类型个数(即返回值和参数的个数)。上例的宏参数分别表示:

    get_peername 函数名字
    CVT_STRING 函数返回值的类型为string
    函数的提示信息 在脚本集成开发环境ucDev可以看到
    CVT_INTEGER 第一个参数,类型为integer
    参数的提示信息 用在ucDev
    CVT_STRING 第二个参数,类型string
    参数提示信息 用在ucDev
    1 有 1 个参数有默认值,即最后一个参数

    脚本调用如下:

    string: strName;

    strName = get_peername(12);  //  Kevin's ID is 12, so strName = "Kevin"
    strName = get_peername(-1);  //  none, strName = "unknown"
    strName = get_peername(-1, "no user");  //  none, strName = "no user"

    最让我激动的是,该脚本把非标准类型的变量全部当作object类型,也就是说任何C++类型都可以接受。下面例子演示如何传递和返回非基本类型的变量。

    UCSFUNC2(get_parentwnd, CVT_OBJECT, "get a parent window",
           CVT_OBJECT,"wnd",
            0)
    {
        ucCalcVar vRet(CVT_OBJECT);
        CWnd * pWnd=NULL;
        //  get CWnd from parameter
        ucAdaptCVT::cvt2out(params[0], pWnd);

        if(pWnd && ::IsWindow(pWnd->GetSafeHwnd()) )
        {
            ucAdaptCVT::out2cvt(pWnd->GetParent(), vRet);
        }
        return vRet;
    }

    4. 结束语

    UCScript是一种面向集成的脚本语言,上面例子是针对C++语言,还支持的其他语言有C#(.Net),Java,Delphi,Visual Basic等等。脚本开发工具可以从http://www.ucscript.com/view_download.asp?id=3下载。初学者可以从http://www.ucscript.com/download.asp下载脚本用户手册。

    (上文转自http://blog.csdn.net/amken/article/details/1491877)


    不同语言之间相互调用函数,这是很普遍的现象,尤其是多种语言并存开发的大项目。一般解决方案是,其中一种通过动态链接库或者COM组件暴露接口,而另一种语言去调用。


    脚本语言调用其它语言,则是通过支持库或者脚本自身支持。就拿VBScript来说,可以通过实现IDispatch接口,脚本创建自动化对象来调用。各种脚本语言都有各自的方式来实现。UCScript是面向集成的脚本语言,这方面跟其它语言来比,更显简练和高效的优势。下面分别从普通函数、类成员函数这两方面介绍UCScript如何调用C++语言的函数。

    1. 普通函数
    在C++一提到函数调用,不得不提到的是调用约定(calling convention)。最常用的有__stdcall,__cdecl,__fastcall等几种方式。


    __stdcall是Windows API函数使用的规范,由被调用函数负责参数从堆栈移走,生成的代码比__cdecl小。this指针压入堆栈。以“?”标识函数名的开始,后跟函数名; 函数名后面以“@@YG”标识参数表的开始,后跟参数表; 参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。


    __cdecl调用约定是C/C++默认的调用规范,由调用函数负责参数从堆栈移走,有利于参数个数可变。this指针压入堆栈。对于传送参数的栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 __cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。


    __fastcall规则同上面的__stdcall调用约定,它的主要特点就是快,因为它是通过寄存器来传送参数的,即用ECX和EDX传送前两个参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。UCScript不支持这种调用的函数。


    __stdcall和__cdecl的最大区别就是用来传递参数的堆栈空间的清除。UCScript底层屏蔽这个差异,也就是说不管是哪一种方式,对UCScript都是一样的。用户不必关心这些细节,可以降低用户使用复杂度。


    int __cdecl strtoint(std::string str) {
    printf("strtoint: str=%s ", str.c_str());
    return atol(str.c_str());
    }


    要让脚本调用这个C语言的函数,只需如下的一行语句即可


    BIND_UCSFUNC2(strtoint, strtoint, CVT_INTEGER, "convert string to int", (std::string*)CVT_OBJECT, "str");


    下面分析上面各个参数的含义。


    BIND_UCSFUNC2 该函数有2个脚本类别(Script Type),返回值和一个参数
    strtoint 脚本可以调用到的函数名字,不必跟C++语言函数相同名字
    strtoint C++语言的函数名字
    CVT_INTEGER 函数返回值是Integer
    "convert string to int" 函数说明,在ucDev编程可以看到该提示
    (std::string*)CVT_OBJECT 参数是Object,并且转换为std::string类型
    "str" 参数说明,在ucDev编程可以看到该提示

    2. 类成员函数
    类成员函数可以分成两类。一类是this指针通过ECX寄存器来传递;另一类是this指针通过参数传递。UCScript的辅助宏通过参数来区别这种区别。如下例:


    class CMyClass {
    public:
    CMyClass() { m_id = 100; }
    int GetId() {
    printf("CMyClass::GetId, id=%d ", m_id);
    return m_id;
    }
    void SetId(int id) {
    printf("CMyClass::SetId, id=%d ", id); m_id = id;
    }
    int m_id;
    };


    要让脚本调用到上面的GetId函数,只需下面语句


    BIND_UCSFUNC_CM1(get_id, CMyClass, CMyClass::GetId, 1, CVT_INTEGER,"get id from a object");


    上面的第4个参数指定this指针的传递方式:1表示标准方式,也就是this通过ECX寄存器传递;0是非标准方式,this通过参数传递。


    当然,要调用这个函数还得传入一个对象,这个对象可以是其它函数的返回值或者用BIND_UCS_GLOBALVAR绑定的全局对象。比如脚本的代码可能如下:


    object: obj;
    // get obj by any way
    ... ...
    int: id=get_id(obj);

    3. 结束语
    UCScript作为一种面向集成的脚本语言,在调用C++语言有自己独特的方式。总结一下有如下特点:
    可以传递任意C++类型,如上例的std::string类
    封装调用约定(calling convention)的差异
    支持类成员函数调用
    简单方便,一行声明语句即可

  • 相关阅读:
    【月度盘点】《金秋10月》
    selenium简单使用
    数据解析模块BeautifulSoup简单使用
    爬虫简介
    SQLAlchemy简介
    Flask Blueprint
    Flask基于websocket的简单聊天室
    Flask send_file request
    初识Flask
    Python pip简单使用
  • 原文地址:https://www.cnblogs.com/zcwsmile/p/4027809.html
Copyright © 2011-2022 走看看