posts - 12,  comments - 6,  trackbacks - 0
这几天搞了一个套lua与C++相互调用的框架,是之前单位项目中提取出来的,比较成型,在已上线的端游中使用,使用第三方库tolua。
这里使用lua 5.1.5、tolua++ 1.0.92、boost 1.68.0、zlib。
开发环境:vs 2017 、lua调试插件 BabeLua。
lua-5.1.5.zip
toluapp-master.zip
BabeLua-For-2017-V3.2.2.0.zip
先贴图,介绍一下工程目录。

lua项目;lua代码 —— 编译lua.lib
tolua项目:tolua++代码 —— 编译 tolua.lib 和 tolua.exe
LuaProject项目:lua和c++相互交互工程。
LuaScript项目:插件BabeLua 创建,lua脚本编写、调试使用。
lua项目 (vs2017中搭建lua5.1)
创建静态链接库的空工程、添加lua代码文件。
修改工程配置如图(窃取两图)
编辑生成 lua.lib
tolua项目
创建空工程、添加tolua代码文件。
这里注意的是 tolua\src 目录下有 bin 和 lib 路径代码。分别控制编译 exe 和 lib文件。
我这里为了方便,将bin和lib代码同时加入tolua工程。
然后控制 配置类型来生成 应用程序(.exe) 和 静态库(.lib)
修改工程配置
附加包含目录:..\include;..\..\lua\src;
附加依赖项:lua.lib
预处理器定义:_CRT_SECURE_NO_WARNINGS
编译生成 tolua.lib 和 tolua.exe
LuaProject项目
看一下工程代码如图
还挺多,不要慌,这里主要实现的读取的文件可以是zip文件(压缩以后的lua文件)。
主要类也就是 ScriptVmArgs类 和 ScriptVM类 来实现的 lua与c++ 的交互。
这里贴出代码如下:


ScriptVmArgs.h
/*************************************************
vic.MINg        2018/09/18
***********************************************
*/

#pragma once
#include <string>
//  "boost/config/user.hpp" 取消 BOOST_ALL_NO_LIB 注释
#define  BOOST_ALL_NO_LIB
#include "boost/tuple/tuple.hpp"



struct lua_State;

class ScriptVmArgs
{
    friend class ScriptVM;

    class Args
    {
    public:
        virtual int Size()const = 0;
    };

    class ArgsInput
        :public Args
    {
    public:
        virtual void Push(lua_State& lua)const = 0;
    };

    class ArgsOutput
        :public Args
    {
    public:
        virtual void Pop(lua_State& lua) = 0;
    };

    template<typename T>
    class ArgsInputImp
        :public ArgsInput
    {
    public:
        ArgsInputImp(const T& args) :m_args(args) {}
        virtual void Push(lua_State& lua)const
        {
            Push(lua, m_args);
        }
        virtual int Size()const
        {
            return boost::tuples::length<T>::value;
        }

    private:
        static void Push(lua_State& lua, const boost::tuples::null_type& args) {}
        template<typename Head, typename Tail>
        static void Push(lua_State& lua, const boost::tuples::cons<Head, Tail>& args)
        {
            // 这里巧妙的使用了 boost::tuple 的 args.get_head() 和  args.get_tail() 方法,两行代码实现了 自身的递归调用。
            DoPush(lua, args.get_head());
            Push(lua, args.get_tail());
        }

        const T& m_args;
    };

    template<typename T>
    class ArgsOutputImp
        :public ArgsOutput
    {
    public:
        ArgsOutputImp(T& args) :m_args(args) {}
        virtual void Pop(lua_State& lua)
        {
            Pop(lua, m_args, 0);
        }
        virtual int Size()const
        {
            return boost::tuples::length<T>::value;
        }

    private:
        static void Pop(lua_State& lua, const boost::tuples::null_type& args, int idx) {}
        template<typename Head, typename Tail>
        static void Pop(lua_State& lua, const boost::tuples::cons<Head, Tail>& args, int idx)
        {
            //if (args.get_head())
            DoPop(lua, args.get_head(), idx - boost::tuples::length<T>::value);
            Pop(lua, args.get_tail(), idx + 1);
        }

        T& m_args;
    };

    static void DoPush(lua_State& lua, const char* pcArg);
    static void DoPush(lua_State& lua, bool bArg);
    static void DoPush(lua_State& lua, float fArg);
    static void DoPush(lua_State& lua, int nArg);
    static void DoPush(lua_State& lua, unsigned uArg);
    static void DoPop(lua_State& lua, std::string& sArg, int idx);
    static void DoPop(lua_State& lua, bool& bArg, int idx);
    static void DoPop(lua_State& lua, float& fArg, int idx);
    static void DoPop(lua_State& lua, int& nArg, int idx);
    static void DoPop(lua_State& lua, unsigned& uArg, int idx);
};

ScriptVM.h
/*************************************************
vic.MINg        2018/09/18
***********************************************
*/

#pragma once

#include <vector>
#include <map>
#include <string>

#include "ScriptVmArgs.h"

struct lua_State;

struct lua_Debug;
typedef void(*lua_Hook) (lua_State *L, lua_Debug *ar);
typedef int(*lua_CFunction) (lua_State *L);


class ScriptVM
{
public:

    ScriptVM(void);
    virtual ~ScriptVM(void);

    bool    Init(void);
    void    Destroy(void);
    void    ExecuteScriptFile(const char * sScriptFileName, bool bForceReload = falsebool bAssertOnError = true);
    void    ExecuteScript(const char * sScript, bool bAssertOnError = true);
    bool    ExecuteScriptFunc(const std::vector<const char *>& modules, const char * func, bool bAllowNonexist, const char * sig = "", );
    template<typename InTuple, typename OutTuple>
    void ExecuteFunc(const char* func, bool bAllowNonexist, const InTuple& inTuple, OutTuple& outTuple, const std::vector<const char*>* pModuls = 0);
    void    ExposeGlobalUserdata(void * va, const char * name, const char * type);
    void *    GetGlobalUserdata(const char * name, const char * verify_type = NULL);
    void *    GetUserdata(const std::vector<const char *>& modules, const char * name, const char * verify_type = NULL);
    double    GetGlobalNumber(const char * name);
    // 是否存在某变量
    bool    ExistVariable(const std::vector<const char *>& modules, const char * name);

    // 得到某个Lua变量
    bool    GetNumber(const std::vector<const char *>& modules, const char * name, double& dValue);

    //void    OnEndofUpdate(); //call back function on end of game update
    void *    CreateObjectByTypeName(const char * sTypeName);//由tolua注册过的类名创建对象
    double    GetGlobalTableNumber(const char *sTableName, const char* key);//Wang Hongliang:
    double ComputingFormula(const char* sFormula, const std::map<std::stringdouble>& kParams);// 计算数学公式
    lua_State * GetLuaState() { return m_pLua; }

    // 设置脚本钩子,只在调试时可用。 
    void    SetHook(lua_Hook func, int nMask, int nCount);
    void    RegistGlobalFunction(const char* sFunctionName, lua_CFunction pFunction);


    // 这里通过 自己写的io流来处理的 文件读写,把文件内容读到了buff 中 
    
// 我这里支撑压缩文件的读写,你可以自己实现一下文件读写方法 重写该方法即可
    static bool LoadFileToBuffer(const char * filename, unsigned char *& buff, int& size, bool bAssertOnError);
    std::string GetScriptReturnString(std::string sScript);
    
    
    //static sigslot::signal1<const char*> LuaPrinted;

    
// 获取脚本当前所占用的内存
    int        GetConsumeMemoryCount();

    // 打印所有全局变量
    void    PrintGlobalVariables();

protected:
    void DoExecuteFunc(const char* func, bool bAllowNonexist, const  ScriptVmArgs::ArgsInput& input, ScriptVmArgs::ArgsOutput& output, const std::vector<const char*>* pModuls);

    lua_State* m_pLua;
};

template<typename InTuple, typename OutTuple>
inline void ScriptVM::ExecuteFunc(const char* func, bool bAllowNonexist, const InTuple& inTuple, OutTuple& outTuple, const std::vector<const char*>* pModuls /* = 0 */)
{
    // Pass as ArgsInput and ArgsOutput, not ArgsInputImp and ArgsOutputImp. So that the body of DoExecuteFunc() can be placed in .cpp rather than .h.
    DoExecuteFunc(
        func,
        bAllowNonexist,
        ScriptVmArgs::ArgsInputImp<InTuple>(inTuple),
        (ScriptVmArgs::ArgsOutput&)ScriptVmArgs::ArgsOutputImp<OutTuple>(outTuple),            // 为啥子 必须强制转换一下 ScriptVmArgs::ArgsOutput& 上面的代码就不用转换,没整明白
        pModuls
    );
}

ScriptVM.cpp
#include "ScriptVM.h"
#include "IOServer.h"
#include "FileStream.h"

extern "C"
{
    #include "lua.h"  
    #include "lualib.h"  
    #include "lauxlib.h"  
}
#include "tolua++.h"

#include<stdio.h>
#include <assert.h>
#include <sstream>

#define DEBUG_STACK 0

//end of tolua bind declare
extern void tolua_open_binding(lua_State * pLua);
static void stackDump(lua_State *pLua);

//sigslot::signal1<const char*> ScriptVM::LuaPrinted;

//error_msg handling function
static void error_msg(bool bUseAssert, const char * pacFmt, )
{
    //#if defined(_DEBUG)
    char acTemp[2048];

    va_list args;
    va_start(args, pacFmt);
    vsprintf_s(acTemp, pacFmt, args);
    va_end(args);
    if (bUseAssert)
    {
        assert( acTemp);
    }
    //#endif
}

void report_last_error(lua_State *pLua, bool bUseAssert)
{
    lua_getglobal(pLua, "_ALERT");
    error_msg(bUseAssert, "%s\n", lua_tostring(pLua, -2));
    error_msg(bUseAssert, "%s\n", lua_tostring(pLua, -1));
    lua_pop(pLua, 2);  /* remove error_msg message and _ALERT */
}


std::string & std_string_format(std::string & _str, const char * _Format, ) {
    std::string tmp;

    va_list marker = NULL;
    va_start(marker, _Format);

    size_t num_of_chars = _vscprintf(_Format, marker);

    if (num_of_chars > tmp.capacity()) {
        tmp.resize(num_of_chars + 1);
    }

    vsprintf_s((char *)tmp.data(), tmp.capacity(), _Format, marker);

    va_end(marker);

    _str = tmp.c_str();
    return _str;
}


ScriptVM::ScriptVM(void)
{
}

ScriptVM::~ScriptVM(void)
{
}

static int PrintStringList(lua_State * pLua) {

    int n = lua_gettop(pLua);  /* number of arguments */
    int i;
    lua_getglobal(pLua, "tostring");
    std::string out;
    for (i = 1; i <= n; i++) {
        const char *s;
        lua_pushvalue(pLua, -1);  /* function to be called */
        lua_pushvalue(pLua, i);   /* value to print */
        lua_call(pLua, 1, 1);
        s = lua_tostring(pLua, -1);  /* get result */
        if (s == NULL)
            return luaL_error(pLua, "`tostring' must return a string to `print'");
        if (i > 1)
        {
            out += "\t";
            out += s;
        }
        else
            out += s;

        lua_pop(pLua, 1);  /* pop result */
    }
    out += "\n";

    return 0;
}

bool ScriptVM::Init(void)
{
    m_pLua = luaL_newstate();

    lua_cpcall(m_pLua, luaopen_base, 0);
    lua_cpcall(m_pLua, luaopen_io, 0);
    lua_cpcall(m_pLua, luaopen_string, 0);
    lua_cpcall(m_pLua, luaopen_table, 0);
    lua_cpcall(m_pLua, luaopen_math, 0);
    lua_cpcall(m_pLua, luaopen_debug, 0);
    lua_cpcall(m_pLua, luaopen_os, 0);
    lua_cpcall(m_pLua, luaopen_package, 0);

    //luaopen_base(m_pLua);
    
//luaopen_io(m_pLua);
    
//luaopen_table(m_pLua);
    
//luaopen_math(m_pLua);
    
//luaopen_string(m_pLua);
    
//luaopen_debug(m_pLua);

    
//tolua_open_binding(m_pLua);

    
//lua_setgcthreshold(m_pLua, 200); //200k garbage collection threshold
    lua_register(m_pLua, "print", PrintStringList);
    //lua_register(m_pLua, "dofile", OverrideDofile);

    return true;
}

void ScriptVM::Destroy(void)
{
    lua_close(m_pLua);
}

void ScriptVM::ExecuteScriptFile(const char * sScriptName, bool bForceReload /* = false*/bool bAssertOnError /*= true*/)
{
    int nSize1 = lua_gettop(m_pLua);
    //get chunk name as modified script name
    std::string sChunkName(sScriptName);
    for (unsigned int i = 0; i < sChunkName.length(); i++)
    {
        if (sChunkName[i] == '/' || sChunkName[i] == '.')
            sChunkName[i] = '_';
    }

    //get the chunk global
    lua_getglobal(m_pLua, sChunkName.c_str());
    if (bForceReload || !lua_isfunction(m_pLua, -1))//if force reload or not found
    {
        //load it first
        unsigned char * pBuff;
        int nSize;
        if (LoadFileToBuffer(sScriptName, pBuff, nSize, bAssertOnError))
        {
            luaL_loadbuffer(m_pLua, (char *)pBuff, nSize, sScriptName);

            delete[] pBuff;

//            luaL_loadfile(m_pLua, sScriptName);
            lua_setglobal(m_pLua, sChunkName.c_str());
            lua_getglobal(m_pLua, sChunkName.c_str());
        }
        else
            goto failed;
    }

    if (lua_pcall(m_pLua, 0, 0, 0) != 0)
    {
        error_msg(bAssertOnError, "error executing script file %s: ", sScriptName);
        report_last_error(m_pLua, bAssertOnError);
    }

failed:
    lua_settop(m_pLua, nSize1);
}

void ScriptVM::ExecuteScript(const char * sScript, bool bAssertOnError)
{
    int status = luaL_loadbuffer(m_pLua, sScript, strlen(sScript), sScript);
    if (status)
    {
        report_last_error(m_pLua, bAssertOnError);
    }
    else
    {
        status = lua_pcall(m_pLua, 0, LUA_MULTRET, 0);  /* call main */
        if (status)
            report_last_error(m_pLua, bAssertOnError);
    }
}

/*
*    Execute Script Function func in the script. copy/pasted from the book "programming in LUA"
*/
bool ScriptVM::ExecuteScriptFunc(const std::vector<const char *>&modules, const char * func, bool bAllowNonexist, const char * sig, )
{
    bool bIsSuccess = false;

    //PROFILE("ExecuteScriptFunc");
    int nSize1 = lua_gettop(m_pLua);

    //debug
#if DEBUG_STACK
    printf("debug lua: stack size before ExecuteScriptFunc = %d\n", nSize1);
#endif

    va_list    vl;
    int    narg, nres;    /* number of arguments and results */
    va_start(vl, sig);

    //get the actual function
    if (modules.empty()) //func is global
    {
        lua_getglobal(m_pLua, func);
        if (!lua_isfunction(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);
            goto failed;
        }
    }
    else
    {
        //trace down the modules
        std::vector<const char *>::const_iterator it = modules.begin();
        //get the global module name or the actual function name if there is no module
        lua_getglobal(m_pLua, *it);
        if (!lua_istable(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);
            goto failed;
        }

        for (++it; it != modules.end(); ++it)
        {
            lua_pushstring(m_pLua, *it);
            lua_gettable(m_pLua, -2);
            if (!lua_istable(m_pLua, -1))
            {
                if (!bAllowNonexist)
                    error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);
                goto failed;
            }
        }
        //get the func
        lua_pushstring(m_pLua, func);
        lua_gettable(m_pLua, -2);
        if (!lua_isfunction(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);
            goto failed;
        }
    }

    /* push    arguments */
    narg = 0;
    while (*sig) { /* push arguments    */
        switch (*sig++) {
        case 'd': /* double    argument */
        case 'f': /* float    argument */    // NieXu: Treat float as double, same as printf()
            lua_pushnumber(m_pLua, va_arg(vl, double));
            break;
        case 'i': /* int argument */
            lua_pushnumber(m_pLua, va_arg(vl, int));
            break;
        case 's': /* string    argument */
            lua_pushstring(m_pLua, va_arg(vl, char *));
            break;
        case 'b': /* boolean argument */
            lua_pushboolean(m_pLua, va_arg(vl, bool));
            break;
        case 'u': /* light user data */
            lua_pushlightuserdata(m_pLua, va_arg(vl, void *));
            break;
        case 't': /* type user data */
        {
            void* pData = va_arg(vl, void *);
            const char* sType = va_arg(vl, const char*);
            tolua_pushusertype(m_pLua, pData, sType);
            break;
        }

        case '>':
            goto endwhile;
        default:
            error_msg(true, "invalid option (%c)\n", *(sig - 1));
            goto failed;
        }
        narg++;
        luaL_checkstack(m_pLua, 1, "too many    arguments");
    }endwhile:
    /* do the call */
    nres = strlen(sig);    /* number of expected results */
    if (lua_pcall(m_pLua, narg, nres, 0) != 0) /* do    the    call */
    {
        report_last_error(m_pLua, true);
        goto failed;
    }
    /* retrieve    results    */
    nres = -nres; /* stack index of    first result */
    while (*sig)
    { /* get results */
        switch (*sig++)
        {
        case 'd': /* double    result */
            if (!lua_isnumber(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, double *) = lua_tonumber(m_pLua, nres);
            break;
        case 'f': /* float    result */
            if (!lua_isnumber(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, float*) = (float)lua_tonumber(m_pLua, nres);
            break;
        case 'i': /* int result    */
            if (!lua_isnumber(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, int    *) = (int)lua_tonumber(m_pLua, nres);
            break;
        case 's': /* string    result */
            if (!lua_isstring(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, std::string*) = lua_tostring(m_pLua, nres);
            break;
        case 'b': /* boolean argument */
            if (!lua_isboolean(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, bool *) = (0 != lua_toboolean(m_pLua, nres));
            break;
        case 'u': /* light user data */
            if (!lua_isuserdata(m_pLua, nres))
                error_msg(true, "wrong    result type,function name: %s\n", func);
            *va_arg(vl, void **) = lua_touserdata(m_pLua, nres);
            break;
        default:
            error_msg(true, "invalid option (%c)\n", *(sig - 1));
        }
        nres++;
    }

    bIsSuccess = true;
failed:
    va_end(vl);
    //clear the stack
    lua_settop(m_pLua, nSize1);

#if DEBUG_STACK
    //debug
    int nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after ExecuteScriptFunc = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif

    return bIsSuccess;
}

void ScriptVM::ExposeGlobalUserdata(void * va, const char * name, const char * type)
{
    int nSize1 = lua_gettop(m_pLua);
#if DEBUG_STACK
    //debug
    printf("debug lua: stack size before ExposeGlobalUserdata = %d\n", nSize1);
#endif

    tolua_pushusertype(m_pLua, va, type);
    lua_setglobal(m_pLua, name);

    //clear the stack
    lua_settop(m_pLua, nSize1);

#if DEBUG_STACK
    //debug
    int nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after ExposeGlobalUserdata = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif
}

void * ScriptVM::GetGlobalUserdata(const char * name, const char * verify_type /*= NULL*/)
{
    void * pRet = NULL;
    int nSize1 = lua_gettop(m_pLua);
    int nSize2 = 0;
#if DEBUG_STACK
    //debug
    printf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);
#endif

    lua_getglobal(m_pLua, name);

    //verify type
    if (verify_type)
    {
        tolua_Error tolua_err;
        if (
            !tolua_isusertype(m_pLua, 1, verify_type, 0, &tolua_err) ||
            !tolua_isnoobj(m_pLua, 2, &tolua_err)
            )
        {
            tolua_error(m_pLua, "#ferror in function 'ScriptVM::GetGlobalUserdata'.", &tolua_err);
            goto failed;
        }
    }
    
         pRet = tolua_tousertype(m_pLua, -1, 0);
        //clear the stack
        lua_settop(m_pLua, nSize1);




#if DEBUG_STACK
    //debug
    nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after GetGlobalUserdata = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif
    return pRet;

failed:
        //lua_settop(m_pLua,0);
        lua_settop(m_pLua, nSize1);
        return NULL;

}

double ScriptVM::GetGlobalNumber(const char * name)
{
    int nSize1 = lua_gettop(m_pLua);
#if DEBUG_STACK
    //debug
    printf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);
#endif

    lua_getglobal(m_pLua, name);

    double ret = tolua_tonumber(m_pLua, -1, 0);
    //clear the stack
    lua_settop(m_pLua, nSize1);

#if DEBUG_STACK
    //debug
    int nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after GetGlobalUserdata = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif
    return ret;
}

void * ScriptVM::GetUserdata(const std::vector<const char *>& modules, const char * name, const char * verify_type/*= NULL*/)
{
    void * pRet = NULL;
    int nSize1 = lua_gettop(m_pLua);
    int nSize2 = 0;
#if DEBUG_STACK
    printf("debug lua: stack size before GetUserdata = %d\n", nSize1);
#endif
    if (modules.empty()) //userdata is global
    {
        lua_getglobal(m_pLua, name);
    }
    else
    {
        //trace down the modules
        std::vector<const char *>::const_iterator it = modules.begin();
        //get the global module name or the actual function name if there is no module
        lua_getglobal(m_pLua, *it);
        if (!lua_istable(m_pLua, -1))
        {
            error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);
            goto failed;
        }

        for (++it; it != modules.end(); ++it)
        {
            lua_pushstring(m_pLua, *it);
            lua_gettable(m_pLua, -2);
            if (!lua_istable(m_pLua, -1))
            {
                std::vector<const char *>::const_iterator itMsg = modules.begin();
                std::string sMsg;
                for (; itMsg <= it; ++itMsg)
                {
                    sMsg.append(*itMsg);
                    if (itMsg != it)
                    {
                        sMsg.append(".");
                    }
                }
                error_msg(true, "GetUserdata: Invalid table name: %s\n", sMsg.c_str());
                goto failed;
            }
        }
        //get the data
        lua_pushstring(m_pLua, name);
        lua_gettable(m_pLua, -2);
    }

    //verify type
    
//if(verify_type)
    
//{
    
//    tolua_Error tolua_err;
    
//    if (
    
//        !tolua_isusertype(m_pLua,1,verify_type,0,&tolua_err) ||
    
//        !tolua_isnoobj(m_pLua,2,&tolua_err)
    
//        )
    
//    {
    
//        error_msg(m_pLua,"#ferror in function 'ScriptVM:GetUserdata: %s\n", name);
    
//        goto failed;
    
//    }
    
//}

    pRet = tolua_tousertype(m_pLua, -1, 0);
    //clear the stack
    lua_settop(m_pLua, nSize1);

#if DEBUG_STACK
    //debug
    nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after GetUserdata = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif
    return pRet;

failed:
    lua_settop(m_pLua, nSize1);
    return NULL;
}

static void stackDump(lua_State *m_pLua) {
    int i;
    int top = lua_gettop(m_pLua);
    for (i = 1; i <= top; i++) { /* repeat for each level */
        int t = lua_type(m_pLua, i);
        switch (t) {
        case LUA_TSTRING: /* strings */
            printf("`%s'", lua_tostring(m_pLua, i));
            break;
        case LUA_TBOOLEAN: /* booleans */
            printf(lua_toboolean(m_pLua, i) ? "true" : "false");
            break;
        case LUA_TNUMBER: /* numbers */
            printf("%g", lua_tonumber(m_pLua, i));
            break;
        default/* other values */
            printf("%s", lua_typename(m_pLua, t));
            break;
        }
        printf(" "); /* put a separator */
    }
    printf("\n"); /* end the listing */
}

bool ScriptVM::LoadFileToBuffer(const char * filename, unsigned char *& buff, int& size, bool bAssertOnError)
{
    IO::Stream* pStream = IO::IOServer::Instance()->CreateReadStream(filename);
    if (pStream != NULL && pStream->Open())
    {
        size = pStream->GetSize();
        if (size == 0)
        {
            std::string sMsg;
            sMsg.append(filename).append("以上文件中无内容!");
            assert( sMsg.c_str());
        }

        buff = new unsigned char[size];
        pStream->Read(buff, size);

        pStream->Close();

        IO::IOServer::Instance()->ReleaseStream(pStream);

        return true;
    }
    else
    {
        std::string sMsg;
        sMsg.append("[").append(filename).append("]脚本文件不存在!");
        assert( sMsg.c_str());

        if (NULL != pStream)
        {
            IO::IOServer::Instance()->ReleaseStream(pStream);
        }
        return false;
    }
}

//由tolua注册过的类名创建对象
void *    ScriptVM::CreateObjectByTypeName(const char * sTypeName)
{
    //处理lua脚本
    int nSize = (int)strlen(sTypeName) + 20;
    unsigned char* buffer = new unsigned char[nSize];
    sprintf_s((char*)buffer, nSize, "pMyCreatedObj=%s:new()", sTypeName);

    //执行脚本
    ExecuteScript((char*)buffer);

    void* ret = GetGlobalUserdata("pMyCreatedObj");

    return ret;
}

//获得全局表中常量
double ScriptVM::GetGlobalTableNumber(const char *sTableName, const char* key)
{
    double ret = 0;
    int nSize1 = lua_gettop(m_pLua);
    int nSize2 = 0;
#if DEBUG_STACK
    //debug
    printf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);
#endif
    lua_getglobal(m_pLua, sTableName);
    if (!lua_istable(m_pLua, -1))
    {
        error_msg(true, "GetGlobalTableNumber: %s isn't a Lua Table.", sTableName);
        goto failed;
    }

    lua_pushstring(m_pLua, key);
    lua_gettable(m_pLua, -2);
    if (!lua_isnumber(m_pLua, -1))
    {
        error_msg(true, "GetGlobalTableNumber: %s isn't a number.", key);
        goto failed;
    }
    ret = lua_tonumber(m_pLua, -1);
    lua_settop(m_pLua, nSize1);
#if DEBUG_STACK
    //debug
    nSize2 = lua_gettop(m_pLua);
    printf("debug lua: stack size after GetUserdata = %d\n", nSize2);
    if (nSize1 != nSize2)
        stackDump(m_pLua);
#endif

    return ret;

failed:
    lua_settop(m_pLua, nSize1);
    return 0;
}

void ScriptVM::SetHook(lua_Hook func, int nMask, int nCount)
{
    if (!m_pLua)
        return;

    //lua_sethook(m_pLua, func, nMask, nCount);
}

bool ScriptVM::ExistVariable(const std::vector<const char *>& modules, const char * name)
{
    int nSize1 = lua_gettop(m_pLua);

    if (modules.empty()) //userdata is global
    {
        lua_getglobal(m_pLua, name);
    }
    else
    {
        //trace down the modules
        std::vector<const char *>::const_iterator it = modules.begin();
        //get the global module name or the actual function name if there is no module
        lua_getglobal(m_pLua, *it);
        if (!lua_istable(m_pLua, -1))
        {
            error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);
            goto failed;
        }

        for (++it; it != modules.end(); ++it)
        {
            lua_pushstring(m_pLua, *it);
            lua_gettable(m_pLua, -2);
            if (!lua_istable(m_pLua, -1))
            {
                error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);
                goto failed;
            }
        }
        //get the data
        lua_pushstring(m_pLua, name);
        lua_gettable(m_pLua, -2);
    }

    if (lua_isnil(m_pLua, -1))
    {
        goto failed;
    }
    else
    {
        goto success;
    }

failed:
    lua_settop(m_pLua, nSize1);
    return false;

success:
    lua_settop(m_pLua, nSize1);
    return true;
}

// 替换字符串
void string_replace(std::string &strBig, const std::string &strsrc, const std::string &strdst)
{
    std::string::size_type pos = 0;
    std::string::size_type srclen = strsrc.size();
    std::string::size_type dstlen = strdst.size();

    while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
    {
        strBig.replace(pos, srclen, strdst);
        pos += dstlen;
    }
}

double ScriptVM::ComputingFormula(const char* sFormula, const std::map<std::stringdouble>& kParams)
{
    std::string sRealFormula = sFormula;
    if (sRealFormula == "")
    {
        assert(false && "公式错误");
        return 0.0;
    }

    std::string sParamValue = "0";
    std::stringstream kStrStream;

    // 替换参数
    for (std::map<std::stringdouble>::const_iterator it(kParams.begin()); it != kParams.end(); it++)
    {// 得到公式
     
//         kStrStream<<it->second;    
     
//         sParamValue = kStrStream.str();
        sParamValue = std_string_format(sParamValue, "%f", it->second);
        string_replace(sRealFormula, it->first, sParamValue);

        /*
        // 支持在公式中包含的脚本函数调用可以有字符串参数,避免以当前属性名为字符串的参数被属性值替换 [2/18/2011 shuaiwang]
        if (sRealFormula.find('\'') != 0xffffffff)
        {
            std::string sProParam = std::string("\'").append(it->first).append("\'");
            sRealFormula.replace(sProParam, "????");
            sRealFormula.replace(it->first, sParamValue);
            sProParam = std::string("\"").append(it->first).append("\"");
            sRealFormula.replace("????", sProParam);
        }
        else
        {
            sRealFormula.replace(it->first, sParamValue);
        }
        
*/
        
//sRealFormula.Replace(it->first, sParamValue);
    }

    std::string sReturn = "return ";

    if (sRealFormula.find(sReturn) == std::string::npos)
    {
        sRealFormula = sReturn.append(sRealFormula);
    }

    /*std::stringstream kFunStream;

    kFunStream<<"computing_formula_teamfunction = function()\n"
    << sRealFormula << "\n"
    << "end";
*/

    std::string sTempFunction = std::string("computing_formula_teamfunction = function()\n").append(sRealFormula).append("\nend")/*kFunStream.str()*/;

    ExecuteScript(sTempFunction.c_str(), true);

    std::vector<const char *> modules;
    double dValue = 0.0;
    if (ExecuteScriptFunc(modules, "computing_formula_teamfunction", false, ">d", &dValue) == false)
    {
        //_logError2("computing_formula_teamfunction这个问题的来源是:", sFormula << ",请在数据表中修改相应公式!");
    }

    ExecuteScript("computing_formula_teamfunction = nil", true);

    return dValue;
}

std::string ScriptVM::GetScriptReturnString(std::string sScript)
{
    std::string sReturn = "return ";

    if (sScript.find(sReturn) == 0xffffffff)
    {
        sScript = sReturn.append("\"").append(sScript).append("\"");
    }

    std::string sTempFunction = std::string("string_function = function()\n").append(sScript).append("\nend");
    ExecuteScript(sTempFunction.c_str(), true);

    std::vector<const char *> modules;
    std::string sReturnString;
    ExecuteScriptFunc(modules, "string_function", false, ">s", &sReturnString);

    ExecuteScript("string_function = nil", true);
    return sReturnString.c_str();
}

void ScriptVM::RegistGlobalFunction(const char* sFunctionName, lua_CFunction pFunction)
{
    lua_register(m_pLua, sFunctionName, pFunction);
}

bool ScriptVM::GetNumber(const std::vector<const char *>& modules, const char * name, double& dValue)
{
    int nSize1 = lua_gettop(m_pLua);

    if (modules.empty()) //userdata is global
    {
        lua_getglobal(m_pLua, name);
    }
    else
    {
        //trace down the modules
        std::vector<const char *>::const_iterator it = modules.begin();
        //get the global module name or the actual function name if there is no module
        lua_getglobal(m_pLua, *it);
        if (!lua_istable(m_pLua, -1))
        {
            error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);
            lua_settop(m_pLua, nSize1);
            return false;
        }

        for (++it; it != modules.end(); ++it)
        {
            lua_pushstring(m_pLua, *it);
            lua_gettable(m_pLua, -2);
            if (!lua_istable(m_pLua, -1))
            {
                error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);
                lua_settop(m_pLua, nSize1);
                return false;
            }
        }
        //get the data
        lua_pushstring(m_pLua, name);
        lua_gettable(m_pLua, -2);
    }

    if (lua_isnumber(m_pLua, -1))
    {
        dValue = tolua_tonumber(m_pLua, -1, 0);
        lua_settop(m_pLua, nSize1);
        return true;
    }
    else
    {
        lua_settop(m_pLua, nSize1);
        return false;
    }
}

void ScriptVM::DoExecuteFunc(const char* func, bool bAllowNonexist, const ScriptVmArgs::ArgsInput& input, ScriptVmArgs::ArgsOutput& output, const std::vector<const char*>* pModuls)
{

    //get the actual function
    const int nOrgSize = lua_gettop(m_pLua);
    int nInputSize = input.Size();
    int nOutputSize = output.Size();

    if (!pModuls || (pModuls && pModuls->empty())) //func is global
    {
        lua_getglobal(m_pLua, func);
        if (!lua_isfunction(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);
            goto failed;
        }
    }
    else
    {
        //trace down the modules
        std::vector<const char *>::const_iterator it = pModuls->begin();
        //get the global module name or the actual function name if there is no module
        lua_getglobal(m_pLua, *it);
        if (!lua_istable(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);
            goto failed;
        }

        for (++it; it != pModuls->end(); ++it)
        {
            lua_pushstring(m_pLua, *it);
            lua_gettable(m_pLua, -2);
            if (!lua_istable(m_pLua, -1))
            {
                if (!bAllowNonexist)
                    error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);
                goto failed;
            }
        }

        //get the func
        lua_pushstring(m_pLua, func);
        lua_gettable(m_pLua, -2);
        if (!lua_isfunction(m_pLua, -1))
        {
            if (!bAllowNonexist)
                error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);
            goto failed;
        }
    }

    /* push    arguments */
    input.Push(*m_pLua);

    /* do the call */
    if (lua_pcall(m_pLua, nInputSize, nOutputSize, 0) != 0) /* do    the    call */
    {
        report_last_error(m_pLua, true);
        goto failed;
    }

    /* retrieve    results    */
    output.Pop(*m_pLua);

failed:
    //clear the stack
    lua_settop(m_pLua, nOrgSize);
}

int ScriptVM::GetConsumeMemoryCount()
{
    int nCount = 0;
    if (m_pLua)
    {
        nCount = lua_getgccount(m_pLua);
    }

    return nCount;
}

void ScriptVM::PrintGlobalVariables()
{
    ExecuteScript("for n in pairs(_G) do print(n) end", true);
}

void ScriptVmArgs::DoPush(lua_State& lua, const char* pcArg)
{
    lua_pushstring(&lua, pcArg);
}

void ScriptVmArgs::DoPush(lua_State& lua, const bool bArg)
{
    lua_pushboolean(&lua, bArg);
}

void ScriptVmArgs::DoPush(lua_State& lua, const float fArg)
{
    lua_pushnumber(&lua, fArg);
}

void ScriptVmArgs::DoPush(lua_State& lua, const int nArg)
{
    lua_pushnumber(&lua, nArg);
}

void ScriptVmArgs::DoPush(lua_State& lua, unsigned uArg)
{
    lua_pushinteger(&lua, uArg);
}

void ScriptVmArgs::DoPop(lua_State& lua, bool& bArg, int idx)
{
    bArg = lua_toboolean(&lua, idx) != 0;
}

void ScriptVmArgs::DoPop(lua_State& lua, std::string& sArg, int idx)
{
    sArg = lua_tostring(&lua, idx);
}

void ScriptVmArgs::DoPop(lua_State& lua, float& fArg, int idx)
{
    fArg = (float)lua_tonumber(&lua, idx);
}

void ScriptVmArgs::DoPop(lua_State& lua, int& nArg, int idx)
{
    nArg = (int)lua_tonumber(&lua, idx);
}

void ScriptVmArgs::DoPop(lua_State& lua, unsigned& uArg, int idx)
{
    uArg = lua_tointeger(&lua, idx);
}


OK,下面来实现一下测试代码,来看看如何 lua 和 c++进行交互的。


我们来简单编写几个文件。

函数调用:
ScriptFunction.h
/**************************************************************************************
        vic.MINg            2018/09/19
**************************************************************************************
*/

#pragma once

namespace Script {
    //tolua_begin
    void show_luaproject_info();
    //tolua_end
}

ScriptFunction.cpp
#include "ScriptFunction.h"
#include <stdio.h>

namespace Script {

    void show_luaproject_info()
    {
        printf("Show LuaProject C++ Info.\n");
    }

}

结构体
Hag.h
/***********************************************************************
        vic. MINg        2018/09/25
**********************************************************************
*/

#pragma once
#include <string>

//tolua_begin
struct Hag        
{
    int            hag_id;
    std::string hag_name;
    bool             is_valid;
    int            hag_score;

    Hag() { }
    virtual ~Hag() {}
};
//tolua_end


Student.h
#pragma once

#include<iostream>
using namespace std;

//tolua_begin
class Student
{
public:

    Student();
    virtual ~Student();

    void Run();
    void Run(int a);
    
};
//tolua_end

Student.cpp
#include "Student.h"


Student::Student()
{
}

void Student::Run()
{
    //cout << "Student Run" << endl;
    printf("Student Student Run.\n");
}

void Student::Run(int a)
{
    //cout << "Student Run" <<a<< endl;
    printf("Student Student Run with %d.\n", a);
}

Student::~Student()
{
}

非常简单的测试代码。不言语。继续往下。
我们需要编写一份 LuaScript.pkg 文件来导出这些文件,让lua可以调用他们。

LuaScript.pkg
#include "tolua++.h"

$#pragma warning(disable : 4800) //forcing value to bool warning


// script functions
$cfile "ScriptFunction.h"
$using namespace Script;


// class
$cfile "Hag.h"
$cfile "Student.h"

做一个实行脚本 glue.bat 来通过 LuaScript.pkg 编译 lua 的cpp文件。

glue.bat
..\..\Debug\tolua -o l_LuaScript.cpp LuaScript.pkg
pause

这样会生成 l_LuaScript.cpp 来实行lua和c++ 之间的相互调用,这里由于是生成出来的 就不粘代码了

值得注意的几个方面:
使用 boost 的时候为了方便 我们使用了 BOOST_ALL_NO_LIB ,这样 就不用编译 boost了
//  "boost/config/user.hpp" 取消 BOOST_ALL_NO_LIB 注释
#define  BOOST_ALL_NO_LIB

注意 tolua中 注释的使用,如果没使用
   //tolua_export、//tolua_begin、//tolua_end 是无法在实行 glue.bat 时生成的 l_LuaScript.cpp 中生成关联映射的。

工程配置:
附加包含目录:..;..\include;..\..\lua\src;..\..\tolua\include;
附加依赖项:lua.lib;tolua.lib;
预处理器定义:WIN32;_CRT_SECURE_NO_WARNINGS;


main.cpp
#include "ScriptVM.h"
#include "ScriptVmArgs.h"
#include <stdio.h>
#include <string>
#include <assert.h>
#include <iostream>

extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
}


#include "tolua++.h"

#include "IOServer.h"
#include "Stream.h"
#include "Hag.h"

#include "FSWrapper.h"

#include "boost/ref.hpp"


extern void report_last_error(lua_State *L, bool bUseAssert);
extern int  tolua_LuaScript_open(lua_State* tolua_S);

static int OverrideDofile(lua_State * L) {

    int n = lua_gettop(L);  /* number of arguments */
    lua_getglobal(L, "tostring");

    if (n != 1)
        return luaL_error(L, "'dofile' param error!");

    std::string sFileName = "";
    lua_pushvalue(L, -1);  /* function to be called */
    lua_pushvalue(L, 1);   /* value to print */
    lua_call(L, 1, 1);
    sFileName = lua_tostring(L, -1);  /* get result */
    if (sFileName == "")
        return luaL_error(L, "'dofile' param error!");

    sFileName = Internal::FSWrapper::GetBinDirectory().c_str();
    sFileName.append("/Scripts/test.lua");
    lua_pop(L, 1);  /* pop result */

                    //get chunk name as modified script name
    std::string sChunkName(sFileName.c_str());
    for (unsigned int i = 0; i < sChunkName.length(); i++)
    {
        if (sChunkName[i] == '/' || sChunkName[i] == '.')
            sChunkName[i] = '_';
    }

    //load it first
    unsigned char * pBuff;
    int nSize;
    if (ScriptVM::LoadFileToBuffer(sFileName.c_str(), pBuff, nSize, true))
    {
        luaL_loadbuffer(L, (char *)pBuff, nSize, sFileName.c_str());

        delete[] pBuff;

        //luaL_loadfile(L, sScriptName);
        lua_setglobal(L, sChunkName.c_str());
        lua_getglobal(L, sChunkName.c_str());

        if (lua_pcall(L, 0, 0, 0) != 0)
        {
            //printf("文件 " << sFileName.c_str() << " 执行错误!");
            std::string sErrorMsg = "error executing script file %s: ";
            sErrorMsg += sFileName;
            assert(sErrorMsg.c_str());
            //error_msg(true, "error executing script file %s: ", sFileName.AsCharPtr());
            report_last_error(L, true);
        }
    }
    else
    {

        lua_settop(L, n);
        return luaL_error(L, "error in the file!");
    }

    return 0;
}

int main(int argc, char** argv) 
{
    ScriptVM* pScriptVM = new ScriptVM;
    if (!pScriptVM->Init())
    {
        printf("ScriptVM Init failed! \n");
        return -1;
    }

    pScriptVM->RegistGlobalFunction("dofile", OverrideDofile);
     //  注意调用 tolua_LuaScript_open ,使lua和c++可以相互调用
    tolua_LuaScript_open(pScriptVM->GetLuaState());

    //  加载一个lua脚本
    pScriptVM->ExecuteScriptFile("scripts/hello.lua");

    
    // 调用 lua 中的 student_run 方法 , ("i", 20) 表示传参, i 表示传一个参数 为int型
    
// 假设要传 两个参数 一个是 double 一个是 std::string 可以写成 ("ds", 3.14, "vic.MINg")
    std::vector<const char *>modules;
    pScriptVM->ExecuteScriptFunc(modules, "student_run", false, "i", 20);

    
    // 调用 lua 中的 add 方法
    
// inTuple 为进参、 outTuple 为出参
    
//  调用方法如下 
    int a = 20, b = 10;
    int  sum = 0;
    boost::tuple<intint> inTuple = boost::tuples::make_tuple(a, b);
    boost::tuple<int&> outTuple = boost::tuples::make_tuple(boost::ref(sum));
    pScriptVM->ExecuteFunc("add", false, inTuple, outTuple);
    printf("Lua Func Add %d + %d = %d \n", a, b, sum);


    // 这里我们 传一个字符串 来在lua中 new一个 新类
    
// 看似比较平庸,我们可以通过某些文件来动态生成一个类 
    
// 比如  我们在 xml 中定义一个 <HagType>PlayerHag</HagType>
    
//  然后 读取该项 然后 类型下面代码 生成 PlayerHag, 然后通过 RTTI 来判断 应用该类 
    std::string strHag = "english_hag = nil english_hag = Hag:new()";
    pScriptVM->ExecuteScript(strHag.c_str());
    Hag* pEnglish_hag = static_cast<Hag*>(pScriptVM->GetGlobalUserdata("english_hag"));
    if (pEnglish_hag)
    {
        pEnglish_hag->hag_id = 1;
        pEnglish_hag->hag_name = "English";
        pEnglish_hag->is_valid = true;
        pEnglish_hag->hag_score = 94;    
    }


    // 通过 lua 快速公式计算
    std::string strFormula = "1.8586*level + 8.7842  +50+(level-1)*5/3";
    std::map<std::stringdouble> mapParams;
    mapParams.insert(std::make_pair( "level", 3));
    double dValue = pScriptVM->ComputingFormula(strFormula.c_str(), mapParams);
    printf("Lua ComputingFormula level = %d, 1.8586*level + 8.7842  +50+(level-1)*5/3 =  %lf \n", 3, dValue);

    system("pause");

    return 0;
}

/*        下面的代码 是处理 原生的 lua脚本的
using namespace std;

void main()
{
    //1.创建Lua状态  
    lua_State *L = luaL_newstate();
    if (L == NULL)
    {
        return;
    }

    //2.加载Lua文件  
    int bRet = luaL_loadfile(L, "E:\\hello.lua");
    if (bRet)
    {
        cout << "load file error" << endl;
        return;
    }

    //3.运行Lua文件  
    bRet = lua_pcall(L, 0, 0, 0);
    if (bRet)
    {
        cout << "pcall error" << endl;
        return;
    }

    //4.读取变量  
    lua_getglobal(L, "str");
    string str = lua_tostring(L, -1);
    cout << "str = " << str.c_str() << endl;        //str = I am so cool~  

                                                    //5.读取table  
    lua_getglobal(L, "tbl");
    lua_getfield(L, -1, "name");
    str = lua_tostring(L, -1);
    cout << "tbl:name = " << str.c_str() << endl; //tbl:name = shun  

                                                  //6.读取函数  
    lua_getglobal(L, "add");        // 获取函数,压入栈中  
    lua_pushnumber(L, 10);          // 压入第一个参数  
    lua_pushnumber(L, 20);          // 压入第二个参数  
    int iRet = lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。  
    if (iRet)                       // 调用出错  
    {
        const char *pErrorMsg = lua_tostring(L, -1);
        cout << pErrorMsg << endl;
        lua_close(L);
        return;
    }
    if (lua_isnumber(L, -1))        //取值输出  
    {
        double fValue = lua_tonumber(L, -1);
        cout << "Result is " << fValue << endl;
    }

    system("pause");
    //至此,栈中的情况是:  
    //=================== 栈顶 ===================   
    //  索引  类型      值  
    //   4   int:      30   
    //   3   string:   shun   
    //   2   table:     tbl  
    //   1   string:    I am so cool~  
    //=================== 栈底 ===================   

    //7.关闭state  
    lua_close(L);
    return;
}
*/

LuaScript项目

这个项目 使用BabeLua插件创建,方便lua脚本的编写和调试。

hello.lua
str = "I am so cool"  
tbl = {name = "shun", id = 20114442}  

function add(a,b)  
    return a + b 
end

function student_run(n)
    local st = Student:new() 
    st:Run(n)
    st:delete()
    show_luaproject_info()
end 
OK 这样一套完善的lua与c++相互交互体系就这样实现了,运行结果如下~~~~~~~~~~~




posted on 2018-09-26 15:53 vic.MINg 阅读(4338) 评论(0)  编辑 收藏 引用 所属分类: C/C++

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理



<2024年3月>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(1)

随笔分类(13)

随笔档案(12)

搜索

  •  

最新评论

阅读排行榜

评论排行榜