前两篇文章中介绍了C++调用lua、lua栈操作的一些相关知识。

下面说一下Lua的工具。我们下一步要用到其中的一个帮助我们的开发,其实,Lua里面有很多简化开发的工具,你可以去www.sourceforge.net去找一下。它们能够帮助你简化C++对象与Lua对象互转之间的代码。这里说几个有名的,当然可能不全。

(lua tinker)如果你的系统在windows下,而且不考虑移植,那么我强烈推荐你去下载一个叫做lua tinker的小工具,整个工具非常简单,一个.h和一个.cpp。直接就可以引用到你的工程中,连独立编译都不用,这是一个韩国人写的Lua与 C++接口转换的类,十分方便,代码简洁(居家旅行,必备良药)。它是基于模板的,所以你可以很轻松的把你的C++对象绑定到Lua中。代码较长,呵呵,有兴趣的朋友可以给我留言索要lua tinker的例子。就不贴在这里了。不过我个人不推荐这个东西,因为它在Linux下是编译不过去的。它使用了一种g++不支持的模板写法,虽然有人在尝试把它修改到Linux下编译,但据我所知,修改后效果较好的似乎还没有。不过如果你只是在  windows下,那就没什么可犹豫的,强烈推荐,你会喜欢它的。

(Luabinder)相信用过Boost库的朋友,或许对这个家伙很熟悉。它是一个很强大的Linux下Lua扩展包,帮你封装了很多Lua的复杂操作,主要解决了绑定C++对象和Lua对象互动的关系,非常强大,不过嘛,对于freeeyes而言,还是不推荐,因为freeeyes很懒,不想为了一个Lua还要去编译一个庞大的boost库,当然,见仁见智,如果你的程序本身就已经加载了boost,那么就应该毫不犹豫的选择它。

(lua++)呵呵,这是我最喜欢,也是我一直用到现在的库,比较前两个而言,lua++的封装性没有那么好,很多东西还是需要一点代码的,不过之所以我喜欢,是因为它是用C写的,可以在windows下和linux下轻松转换。如果鱼与熊掌不能兼得,那么我宁愿选择一个兼顾两者的东西,如果有的话,呵呵。当然,lua++就是这么一个东西,如果你继续看我的文章,或许你也会喜欢它的。

这里我们选择lua++作为我们继续进行下去的垫脚石吧。说到Lua++(http://www.codenix.com/~tolua/),这个东西还是挺有渊源的,请你先下载一个。我教你怎么编译。下面是我下载的lua++的目录结构:

还记得我昨天说过如何编译Lua么,现在请你再做一遍,不同的是,请把lua++的程序包中的src/lib中的所有h和cpp,还有include下的那个.h拷贝到你上次建立的lua工程中。最后记得把tolua++.h放在你的Include文件夹下。下图为整个项目的目录结构(最终运行成功的目录结构):

行了,我们把上次CLuaFn类稍微改一下。

extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "tolua++.h" //这里加一行
};
#include "_ParamData.h"
#include "CTest.h"
class CLuaFn
{
public:
CLuaFn(void);
~CLuaFn(void); void Init(); //初始化Lua对象指针参数
void Close(); //关闭Lua对象指针 bool LoadLuaFile(const char* pFileName); //加载指定的Lua文件
bool CLuaFn::CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut); //执行指定Lua文件中的函数
//参数入栈
bool CLuaFn::PushLuaData(lua_State* pState, _ParamData* pParam);
//参数出栈
bool CLuaFn:: PopLuaData(lua_State* pState, _ParamData* pParam, int nIndex); //新对象的创建、销毁
static int tolua_new_CTest(lua_State* pState);
static int tolua_delete_CTest(lua_State* pState) ;
static int tolua_SetData_CTest(lua_State* pState);
static int tolua_GetData_CTest(lua_State* pState); bool InitClass(); private:
lua_State* m_pState; //这个是Lua的State对象指针,你可以一个Lua文件对应一个 };

行了,这样我们就能用Lua++下的功能了。
昨天,大家看到了 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);这个函数的运用。演示了真么调用Lua函数。
下面,我改一下,这个函数。为什么?还是因为freeeyes很懒,我可不想每有一个函数,我都要写一个C++函数去调用,太累!我要写一个通用的!支持任意函数调用的接口!
于是我创建了两个类。支持任意参数的输入和输出,并打包送给lua去执行

//支持任意参数的输入和输出,并打包送给lua去执行
#ifndef _PARAMDATA_H
#define _PARAMDATA_H #include <vector> #define MAX_PARAM_200 200
using namespace std; struct _ParamData
{
public:
void* m_pParam;
char m_szType[MAX_PARAM_200];
int m_TypeLen;
public:
_ParamData()
{
m_pParam = NULL;
m_szType[] = '\0';
m_TypeLen = ;
}; _ParamData(void* pParam, const char* szType, int nTypeLen)
{
SetParam(pParam, szType, nTypeLen);
}
~_ParamData() {}; void SetParam(void* pParam, const char* szType, int nTypeLen)
{
m_pParam = pParam;
sprintf(m_szType, "%s", szType);
m_TypeLen = nTypeLen;
}; bool SetData(void* pParam, int nLen)
{
if(m_TypeLen < nLen)
{
return false;
} if(nLen > )
{
memcpy(m_pParam, pParam, nLen);
}
else
{
memcpy(m_pParam, pParam, m_TypeLen);
}
return true;
} void* GetParam()
{
return m_pParam;
} const char* GetType()
{
return m_szType;
} bool CompareType(const char* pType)
{
if( == strcmp(m_szType, pType))
{
return true;
}
else
{
return false;
}
}
}; class CParamGroup
{
public:
CParamGroup() {};
~CParamGroup()
{
Close();
}; void Init()
{
m_vecParamData.clear();
}; void Close()
{
for(int i = ; i < (int)m_vecParamData.size(); i++)
{
_ParamData* pParamData = m_vecParamData[i];
delete pParamData;
pParamData = NULL;
}
m_vecParamData.clear();
}; void Push(_ParamData* pParam)
{
if(pParam != NULL)
{
m_vecParamData.push_back(pParam);
}
}; _ParamData* GetParam(int nIndex)
{
if(nIndex < (int)m_vecParamData.size())
{
return m_vecParamData[nIndex];
}
else
{
return NULL;
}
}; int GetCount()
{
return (int)m_vecParamData.size();
} private:
typedef vector<_ParamData*> vecParamData;
vecParamData m_vecParamData;
}; #endif

我创建了两个类,把Lua要用到的类型,数据都封装起来了。这样,我只需要这么改写这个函数。
bool CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut);
它就能按照不同的参数自动给我调用,嘿嘿,懒到家吧!
其实这两个类很简单,_ParamData是参数类,把你要用到的参数放入到这个对象中去,标明类型的大小,类型名称,内存块。而CParamGroup负责将很多很多的_ParamData打包在一起,放在vector里面。
好了,让我们看看CallFileFn函数里面我怎么改的。

#include "CLuaFn.h"

CLuaFn::CLuaFn(void){m_pState = NULL;};        //这句干嘛
CLuaFn::~CLuaFn(void){}; //初始化函数
void CLuaFn::Init()
{
if (NULL == m_pState)
{
m_pState = lua_open(); //返回一个lua对象指针
luaL_openlibs(m_pState); //加载了所有你可能用到的Lua基本库
}
} //关闭Lua对象并释放资源
void CLuaFn::Close()
{
if(NULL != m_pState)
{
lua_close(m_pState);
m_pState = NULL;
}
} bool CLuaFn::LoadLuaFile(const char* pFileName)
{
int nRet = ;
if (NULL == m_pState)
{
printf("[CLuaFn::LoadLuaFile]m_pState is NULL.\n");
return false;
}
//加载文件的时候尽量放在程序的初始化中
nRet = luaL_dofile(m_pState, pFileName);
if (nRet != )
{
printf("[CLuaFn::LoadLuaFile]luaL_loadfile(%s) is file(%d)(%s).\n", pFileName, nRet, lua_tostring(m_pState, -));
return false;
}
return true;
} //参数入栈
bool CLuaFn::PushLuaData(lua_State* pState, _ParamData* pParam)
{
if(pParam == NULL)
{
return false;
} if(pParam->CompareType("string"))
{
lua_pushstring(m_pState, (char* )pParam->GetParam());
return true;
} if(pParam->CompareType("int"))
{
int* nData = (int* )pParam->GetParam();
lua_pushnumber(m_pState, *nData);
return true;
}
else
{
void* pVoid = pParam->GetParam();
// tolua_pushusertype(m_pState, pVoid, pParam->GetType());
return true;
}
} // 参数出栈
bool CLuaFn:: PopLuaData(lua_State* pState, _ParamData* pParam, int nIndex)
{
if(pParam == NULL)
{
return false;
} if(pParam->CompareType("string"))
{
if (lua_isstring(m_pState, nIndex) == )
{
const char* pData = (const char*)lua_tostring(m_pState, nIndex);
pParam->SetData((void* )pData, (int)strlen(pData));
}
return true;
} if(pParam->CompareType("int"))
{
if (lua_isnumber(m_pState, nIndex) == )
{
int nData = (int)lua_tonumber(m_pState, nIndex);
pParam->SetData(&nData, sizeof(int));
}
return true;
}
else
{
pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -);
return true;
}
} //调用函数
bool CLuaFn::CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut)
{
int nRet = ;
int i = ;
if(NULL == m_pState)
{
printf("[CLuaFn::CallFileFn]m_pState is NULL.\n");
return false;
} lua_getglobal(m_pState, pFunctionName); //加载输入参数
for(i = ; i < ParamIn.GetCount(); i++)
{
PushLuaData(m_pState, ParamIn.GetParam(i));
} nRet = lua_pcall(m_pState, ParamIn.GetCount(), ParamOut.GetCount(), );
if (nRet != )
{
printf("[CLuaFn::CallFileFn]call function(%s) error(%s).\n", pFunctionName, lua_tostring(m_pState, -));
return false;
} //获得输出参数
int nPos = ;
for(i = ParamOut.GetCount() - ; i >= ; i--)
{
nPos--;
PopLuaData(m_pState, ParamOut.GetParam(i), nPos);
} int nCount = lua_gettop(m_pState);
//根据返回参数的个数重新设置栈顶, 这样做可以返回任意数量的栈而且清除干净
lua_settop(m_pState, --ParamOut.GetCount()); return true;
} /* ************************************************************* */ int CLuaFn::tolua_new_CTest(lua_State* pState)
{
CTest* pTest = new CTest();
//将一个已经在Lua注册的"CTest"对象指针,压入数据栈
tolua_pushusertype(pState, pTest, "CTest");
return ;
} int CLuaFn::tolua_delete_CTest(lua_State* pState)
{
//将数据栈下的对象以(CTest* )的指针形式弹出来
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
if(NULL != pTest)
{
delete pTest;
}
return ;
} /*tolua_SetData_CTest()函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。
因为我们的SetData方法里面存在变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2, 0);将参数弹出来,
然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数*/
int CLuaFn::tolua_SetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
const char* pData = tolua_tostring(pState, , ); if(pData != NULL && pTest != NULL)
{
pTest->SetData(pData);
}
return ;
} int CLuaFn::tolua_GetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
if(pTest != NULL)
{
char* pData = pTest->GetData();
tolua_pushstring(pState, pData);
}
return ;
} /* ************************************************************* */ bool CLuaFn::InitClass()
{
if(NULL == m_pState)
{
printf("[CLuaFn::InitClass]m_pState is NULL.\n");
return false;
} tolua_open(m_pState);
tolua_module(m_pState, NULL, );
tolua_beginmodule(m_pState, NULL);
tolua_usertype(m_pState, "CTest");
tolua_cclass(m_pState, "CTest", "CTest", "", tolua_delete_CTest);
//只注册一个模块,比如,我们管CTest叫做"CTest",保持和C++的名称一样。这样在Lua的对象库中就会多了一个CTest的对象描述,等同于string,number等等基本类型
tolua_beginmodule(m_pState, "CTest");
tolua_function(m_pState, "new", tolua_new_CTest);
//将Lua里面CTest对象的"SetData"绑定到你的tolua_SetData_CTest()函数中去
tolua_function(m_pState, "SetData", tolua_SetData_CTest);
tolua_function(m_pState, "GetData", tolua_GetData_CTest);
//tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败
tolua_endmodule(m_pState);
tolua_endmodule(m_pState); return true;
}

下面来看一个类(头文件)。假设我们要把这个对象,传输给Lua进行调用。

#ifndef _TEST_H
#define _TEST_H
#include<stdio.h>
class CTest
{
public:
CTest(void);
~CTest(void); char* GetData();
void SetData(const char* pData); private:
char m_szData[];
}; #endif

这个类里面有两个函数,一个是GetData(),一个是SetData(),之所以这么写,我要让Lua不仅能使用我的类,还可以给这个类使用参数。
那么,cpp文件,我们姑且这样写。(当然,你可以进行修改,按照你喜欢的方式写一个方法,呵呵)

 /*这是一个标准的类,我需要这个类在Lua里面可以创造出来,并赋予数值,
甚至我可以把CTest作为一个Lua函数参数,传给Lua函数让它去给我处理*/
#include "CTest.h" CTest::CTest(void){};
CTest::~CTest(void){}; char* CTest::GetData()
{
printf("[CTest::GetData]%s.\n", m_szData);
return m_szData;
} void CTest::SetData(const char* pData)
{
sprintf(m_szData, "%s", pData);
}

这是一个标准的类,我需要这个类在Lua里面可以创造出来,并赋予数值,甚至我可以把CTest作为一个Lua函数参数,传给Lua函数让它去给我处理。让我们来看看怎么做。如果使用标准的Lua语法,有点多,所以我就借用一下上次提到的tolua来做到这一切,我一句句的解释。姑且我们把这些代码放在LuaFn.cpp里面。

/* ************************************************************* */

int CLuaFn::tolua_new_CTest(lua_State* pState)
{
CTest* pTest = new CTest();
//将一个已经在Lua注册的"CTest"对象指针,压入数据栈
tolua_pushusertype(pState, pTest, "CTest");
return ;
} int CLuaFn::tolua_delete_CTest(lua_State* pState)
{
//将数据栈下的对象以(CTest* )的指针形式弹出来
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
if(NULL != pTest)
{
delete pTest;
}
return ;
} /*tolua_SetData_CTest()函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。
因为我们的SetData方法里面存在变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2, 0);将参数弹出来,
然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数*/
int CLuaFn::tolua_SetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
const char* pData = tolua_tostring(pState, , ); if(pData != NULL && pTest != NULL)
{
pTest->SetData(pData);
}
return ;
} int CLuaFn::tolua_GetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, , );
if(pTest != NULL)
{
char* pData = pTest->GetData();
tolua_pushstring(pState, pData);
}
return ;
} /* ************************************************************* */

看看这几个静态函数在干什么。
我要在Lua里面使用CTest,必须让Lua里这个CTest对象能够顺利的创造和销毁。tolua_new_CTest()和tolua_delete_CTest()就是干这个的。
tolua_pushusertype(pState, pTest, "CTest"); 这句话的意思是,将一个已经在Lua注册的"CTest"对象指针,压入数据栈。
同理,CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);是将数据栈下的对象以(CTest* )的指针形式弹出来。
tolua_SetData_CTest()函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。因为我们的SetData方法里面存在变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2, 0);将参数弹出来,然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数。随你的喜好。这里只做一个举例。
好了,你一定会问,这么多的静态函数,用在哪里?呵呵,当然是给Lua注册,当你把这些数据注册到Lua里面,你就可以轻松的在Lua中使用它们。
让我们看看,注册是怎么做到的。
还是在CLuaFn类里面,我们增加一个函数。比如叫做bool InitClass();

bool CLuaFn::InitClass()
{
if(NULL == m_pState)
{
printf("[CLuaFn::InitClass]m_pState is NULL.\n");
return false;
} tolua_open(m_pState);
tolua_module(m_pState, NULL, );
tolua_beginmodule(m_pState, NULL);
tolua_usertype(m_pState, "CTest");
tolua_cclass(m_pState, "CTest", "CTest", "", tolua_delete_CTest);
//只注册一个模块,比如,我们管CTest叫做"CTest",保持和C++的名称一样。这样在Lua的对象库中就会多了一个CTest的对象描述,等同于string,number等等基本类型
tolua_beginmodule(m_pState, "CTest");
tolua_function(m_pState, "new", tolua_new_CTest);
//将Lua里面CTest对象的"SetData"绑定到你的tolua_SetData_CTest()函数中去
tolua_function(m_pState, "SetData", tolua_SetData_CTest);
tolua_function(m_pState, "GetData", tolua_GetData_CTest);
//tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败
tolua_endmodule(m_pState);
tolua_endmodule(m_pState); return true;
}

上面的代码,就是我把上面的几个静态函数,绑定到Lua的基础对象中去。
tolua_beginmodule(m_pState, "CTest");是只注册一个模块,比如,我们管CTest叫做"CTest",保持和C++的名称一样。这样在Lua的对象库中就会多了一个CTest的对象描述,等同于string,number等等基本类型,同理,你也可以用同样的方法,注册你的MFC类。是不是有点明白了?这里要注意,tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败。
tolua_function(m_pState, "SetData", tolua_SetData_CTest);指的是将Lua里面CTest对象的"SetData"绑定到你的tolua_SetData_CTest()函数中去。

好的,让我们来点激动人心的东西。还记得我们的Simple.lua的文件么。我们来改一下它。

function func_Add(x, y)
-- New了一个CTest对象,并进行赋值操作,最后把结果打印在屏幕上
local test = CTest:new()
test:SetData("I'm xiaotian")
test:GetData() return x..y;
end

我在这个函数里面,New了一个CTest对象,并进行赋值操作,最后把结果打印在屏幕上。你或许会问,最后一句不是x+y么,怎么变成了x..y,呵呵,在Lua中,..表示联合的意思,就好比在C++里面, string strName += "freeeyes"。原来觉得x+y有点土,索性返回一个两个字符串的联合吧。
好了,我们已经把我们的这个CTest类注册到了Lua里面,让我们来调用一下吧。修改一下Main函数。变成以下的样子。

#include "CLuaFn.h"
int main(int argc, char* argv[])
{
CLuaFn CLuaFn;
CLuaFn.Init();
CLuaFn.InitClass();
CLuaFn.LoadLuaFile("Sample.lua");
CParamGroup ParamIn;
CParamGroup ParamOut;
char szData1[] = {'\0'}; sprintf(szData1, "[yang]");
_ParamData* pParam1 = new _ParamData(szData1, "string", (int)strlen(szData1));
ParamIn.Push(pParam1); char szData2[] = {'\0'};
sprintf(szData2, "[xiaotian]");
_ParamData* pParam2 = new _ParamData(szData2, "string", (int)strlen(szData2));
ParamIn.Push(pParam2); char szData3[] = {'\0'};
_ParamData* pParam3 = new _ParamData(szData3, "string", );
ParamOut.Push(pParam3); CLuaFn.CallFileFn("func_Add", ParamIn, ParamOut);
char* pData = (char* )ParamOut.GetParam()->GetParam();
printf("[Main]Sum = %s.\n", pData);
getchar();
return ;
}

运行一下:

(使用lua++)Lua脚本和C++交互(三)的更多相关文章

  1. Python导出Excel为Lua/Json/Xml实例教程(三):终极需求

    相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 Python导出E ...

  2. C# 运行中 Lua 语言脚本

    这里就不介绍Lua语言了,使用挺广的一种脚本语言.自行百度. 第一步 使用 Nuget 安装引用 VikingErik.LuaInterface. 第二步 添加 Using using LuaInte ...

  3. lua解析脚本过程中的关键数据结构介绍

    在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...

  4. <转>lua解析脚本过程中的关键数据结构介绍

    在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...

  5. [译] Closures in Lua - Lua中的闭包

    原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...

  6. [转][译] Closures in Lua - Lua中的闭包

    http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...

  7. centos shell编程4【分发系统】 服务器标准化 mkpasswd 生成密码的工具 expect讲解 expect传递参数 expect自动同步文件 expect指定host和要同步的文件 expect文件分发系统 expect自动发送密钥脚本 Linux脚本执行方式 第三十八节课

    centos shell编程4[分发系统] 服务器标准化  mkpasswd 生成密码的工具  expect讲解   expect传递参数   expect自动同步文件  expect指定host和要 ...

  8. [Sciter系列] MFC下的Sciter–3.Sciter脚本与底层交互

    [Sciter系列] MFC下的Sciter–3.Sciter脚本与底层交互,脚本调用底层自定义的方法函数. 本系列文章的目的就是一步步构建出一个功能可用,接口基本完善的基于MFC框架的SciterF ...

  9. Linux 下 expect 脚本语言中交互处理常用命令

    Linux 下 expect 脚本语言中交互处理常用命令 1. #!/usr/bin/expect 告诉操作系统脚本里的代码使用那一个 shell 来执行.这里的 expect 其实和 Linux 下 ...

  10. PHP设置脚本最大执行时间的三种方法

    php.ini 中缺省的最长执行时间是 30 秒,这是由 php.ini 中的 max_execution_time 变量指定,如果脚本需要跑很长时间,例如要大量发送电子邮件,或者分析统计大量数据,服 ...

随机推荐

  1. Hibernate xml配置方法之联合主键

    1.StudentPK类,存放Student的联合主键,必须实现java.io.Serializable接口(为了序列化扩充移植),必须重写equals跟hashCode方法(为了确保唯一性) pub ...

  2. am335x ti SDK6.0 kernel 时钟源码文件记录

    源码流程记录 板级文件开始 // arch/arm/mach-omap2/board-aplex_cmi_at101.c MACHINE_START(APLEX_CMI_AT101, "ap ...

  3. Maven_POM配置结构

    本文转载,转载地址:http://blog.csdn.net/ithomer/article/details/9332071 <project>    <parent>     ...

  4. 【采集层】Kafka 与 Flume 如何选择

    转自:http://my.oschina.net/frankwu/blog/355298 采集层 主要可以使用Flume, Kafka两种技术. Flume:Flume 是管道流方式,提供了很多的默认 ...

  5. 【转】Microsoft .Net Remoting之Marshal、Disconnect与生命周期以及跟踪服务

    Marshal.Disconnect与生命周期以及跟踪服务 一.远程对象的激活 在Remoting中有三种激活方式,一般的实现是通过RemotingServices类的静态方法来完成.工作过程事实上是 ...

  6. 第二百八十九节,MySQL数据库-ORM之sqlalchemy模块操作数据库

    MySQL数据库-ORM之sqlalchemy模块操作数据库 sqlalchemy第三方模块 sqlalchemysqlalchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API ...

  7. 在C语言中实现面向对象(2)

    C语言是结构化和模块化的语言,它是面向过程的.但它也可以模拟C++实现面向对象的功能.那么什么是对象呢?对象就是一个包含数据以及于这些数据有关的操作的集合,也就是包含数据成员和操作代码(即成员函数). ...

  8. C++字符串转化为数字的库函数

    原文链接:http://blog.csdn.net/tsinfeng/article/details/5844838 1.atoi 功 能:把一字符串转换为整数 用 法:int atoi(const ...

  9. 【Java面试题】56 在JAVA中如何跳出当前的多重嵌套循环?

    在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环.例如, public class xunhuan { pu ...

  10. 安装 oracle [转]

    先下载3个东西:链接忘记了,大家自己找一下 1  ORA+11+G+R2+server+64bit+for+windows.iso  (Oracle 安装文件) 2  PLSql 3  oracle6 ...