C++使用lua

C++中使用Lua()


例六,使用C++包装类

    尽管用Lua的C API已经可以方便地写出与Lua交互的程序了,不过对于用惯C++的人来说还是更愿意用C++的方式来解决问题。于是开源社区就出现了不少Lua C API的C++的wrap,比如:LuaBind,LuaPlus,toLua
    这里介绍的是LuaBind库,下载
    它在Windows下貌似只能支持MSVC和ICC,好在Lua可以支持动态库的载入,所以用VC+LuaBind写Lua库,再用C++Builder调用也是个好主意。
    在VC使用LuaBind的方法是把LuaBind的src目录下的cpp文件加入工程(当然也可以先编译成静态库),加入Lua库,设置LuaBind,Lua和Boost的头文件路径。

头文件:

1.  //Lua头文件

2.  extern "C"

3.  {

4.  #include <lua.h>

5.  #include <lualib.h>

6.  #include <lauxlib.h>

7.  }

8.  //LuaBind头文件

9.  #include <luabind/luabind.hpp> 

 

在C++中调用Lua函数

    调用Lua函数那是最简单不过的事情了,用LuaBind的call_function()模板函数就可以了:

1.  int main(

2.    // 建立新的Lua环境

3.    lua_State *myLuaState = luaL_newstate();

4.   

5.    // 让LuaBind“认识”这个Lua环境

6.    luabind::open(myLuaState);

7.   

8.    // 定义一个叫add的Lua函数

9.    luaL_dostring(

10.    myLuaState,

11.    "function add(first, second) "

12.    "  return first + second "

13.    "end "

14.  );

15.   

16.  //调用add函数

17.  cout << "Result: "

18.       << luabind::call_function<int>(myLuaState, "add", 2, 3)

19.       << endl;

20. 

21.  lua_close(myLuaState);

22.}


在本例中我们先使用Lua C API产生一个Lua线程环境,然后调用luabind::open()让LuaBind关联这个线程环境,在使用LuaBind之前这步是必须做的,它要在Lua环境中注册一些LuaBind专用的数据。
在执行完Lua代码之后,我们使用luabind::call_function<int>调用了Lua里的add函数,返回值是int

在Lua代码中调用C++函数

    从前面的文章里我们知道在Lua调用C函数需要经常操作栈,而LuaBind帮我们做了这些工作,下面的例子把print_hello函数送给Lua脚本调用:

1.  void print_hello(int number) {

2.    cout << "hello world " << number << endl;

3.  }

4.   

5.  int main(

6.    // 建立新的Lua环境

7.    lua_State *myLuaState = lua_open();

8.   

9.    // 让LuaBind“认识”这个Lua环境

10.  luabind::open(myLuaState);

11. 

12.  // 添加print_hello函数到Lua环境中

13.  luabind::module(myLuaState) [

14.    luabind::def("print_hello", print_hello)

15.  ];

16. 

17.  // 现在Lua中可以调用print_hello了

18.  luaL_dostring(

19.    myLuaState,

20.    "print_hello(123) "

21.  );

22. 

23.  lua_close(myLuaState);

24.}


    向Lua环境加入函数或其它东东的方法是:

  luabind::module(lua_State* L, char const* name = 0) [

    ...

  ];

    其中module函数中的第二个指定要加入的东东所处的名空间(其实就是table),如果为0,则处于全局域之下。
    在中括号里的luabind::defprint_hello函数提供给Lua环境,第一个参数是Lua中使用的函数名。
    如果要定义多个函数,可以使用逗号分隔。

在Lua代码中使用C++类

    如果我们直接使用Lua C API向Lua脚本注册一个C++类,一般是使用userdata+metatable的方法,就象我们在例五中做的一样。这样做尽管难度不大,却非常繁琐而且不方便维护。
    使用LuaBind我们就可以更方便地向Lua脚本注册C++类了,例:

1.  class NumberPrinter {

2.    public:

3.      NumberPrinter(int number) :

4.        m_number(number) {}

5.   

6.      void print() {

7.        cout << m_number << endl;

8.      }

9.   

10.  private:

11.    int m_number;

12.};

13. 

14.int main() {

15.  lua_State *myLuaState = lua_open();

16.  luabind::open(myLuaState);

17. 

18.  // 使用LuaBind导出NumberPrinter类

19.  luabind::module(myLuaState) [

20.    luabind::class_<NumberPrinter>("NumberPrinter")

21.      .def(luabind::constructor<int>())

22.      .def("print", &NumberPrinter::print)

23.  ];

24. 

25.  // 现在Lua中可以使用NumberPinter类了

26.  luaL_dostring(

27.    myLuaState,

28.    "Print2000 = NumberPrinter(2000) "

29.    "Print2000:print() "

30.  );

31. 

32.  lua_close(myLuaState);

33.}


为了注册一个类,LuaBind提供了class_类。它有一个重载过的成员函数 def() 。这个函数被用来注册类的成员函数、操作符、构造器、枚举和属性。
它将返回this指针,这样我们就可以方便地直接注册更多的成员。

属性

LuaBind 也可以导出类成员变量:

1.  template<typename T>

2.  struct Point {

3.    Point(T X, T Y) :

4.      X(X), Y(Y) {}

5.   

6.    T X, Y;

7.  };

8.   

9.  template<typename T>

10.struct Box {

11.  Box(Point<T> UpperLeft, Point<T> LowerRight) :

12.    UpperLeft(UpperLeft), LowerRight(LowerRight) {}

13. 

14.  Point<T> UpperLeft, LowerRight;

15.};

16. 

17.int main() {

18.  lua_State *myLuaState = lua_open();

19.  luabind::open(myLuaState);

20. 

21.  // 使用LuaBind导出Point<float>类和Box<float>类

22.  luabind::module(myLuaState) [

23.    luabind::class_<Point<float> >("Point")

24.      .def(luabind::constructor<float, float>())

25.      .def_readwrite("X", &Point<float>::X)

26.      .def_readwrite("Y", &Point<float>::Y),

27. 

28.    luabind::class_<Box<float> >("Box")

29.      .def(luabind::constructor<Point<float>, Point<float> >())

30.      .def_readwrite("UpperLeft", &Box<float>::UpperLeft)

31.      .def_readwrite("LowerRight", &Box<float>::LowerRight)

32.  ];

33. 

34.  // 现在Lua中可以使用为些类了

35.  luaL_dostring(

36.    myLuaState,

37.    "MyBox = Box(Point(10, 20), Point(30, 40)) "

38.    "MyBox.UpperLeft.X = MyBox.LowerRight.Y "

39.  );

40. 

41.  lua_close(myLuaState);

42.}


本例中使用def_readwrite定义类成员,我们也可以用def_readonly把类成员定义成只读。

LuaBind还可以把C++类导出成支持getter和setter的属性的Lua类:

1.  struct ResourceManager {

2.    ResourceManager() :

3.      m_ResourceCount(0) {}

4.   

5.    void loadResource(const string &sFilename) {

6.      ++m_ResourceCount;

7.    }

8.    size_t getResourceCount() const {

9.      return m_ResourceCount;

10.  }

11. 

12.  size_t m_ResourceCount;

13.};

14. 

15.int main() {

16.  lua_State *myLuaState = lua_open();

17.  luabind::open(myLuaState);

18. 

19.  // 导出类,在Lua中调用ResourceCount属性会调用C++中的ResourceManager::getResourceCount

20.  // 属性定义有点象C++Builder里的__property定义,呵呵

21.  luabind::module(myLuaState) [

22.    luabind::class_<ResourceManager>("ResourceManager")

23.      .def("loadResource", &ResourceManager::loadResource)

24.      .property("ResourceCount", &ResourceManager::getResourceCount)

25.  ];

26. 

27.  try {

28.    ResourceManager MyResourceManager;

29. 

30.    // 把MyResourceManager定义成Lua的全局变量

31.    luabind::globals(myLuaState)["MyResourceManager"] = &MyResourceManager;

32. 

33.    // 调用

34.    luaL_dostring(

35.      myLuaState,

36.      "MyResourceManager:loadResource(\"abc.res\") "

37.      "MyResourceManager:loadResource(\"xyz.res\") "

38.      " "

39.      "ResourceCount = MyResourceManager.ResourceCount "

40.    );

41. 

42.    // 读出全局变量

43.    size_t ResourceCount = luabind::object_cast<size_t>(

44.      luabind::globals(myLuaState)["ResourceCount"]

45.    );

46.    cout << ResourceCount << endl;

47.  }

48.  catch(const std::exception &TheError) {

49.    cerr << TheError.what() << endl;

50.  }

51. 

52.  lua_close(myLuaState);

53.}

附: Lua语法简介

1.语法约定

    Lua语句用分号结尾,不过如果不写分号,Lua也会自己判断如何区分每条语句
    如:
        a=1 b=a*2 --这样写没有问题,但不太好看。
    建议一行里有多个语句时用分号隔开

    变量名、函数名之类的命名规则与C语言一样:由字母,下划线和数字组成,但第一个字符不能是数字。并且不能和Lua的保留字相同。
    
    Lua是大小写敏感的
    
    使用两个减号--作为单行注释符,多行注释使用--[[...--]]
   

2.类型

    Lua是动态类型语言,变量不要类型定义。Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table。
    同一变量可以随时改变它的类型,如:

1.  a = 10                  --number

2.  a = "hello"             --string

3.  a = false               --boolean

4.  a = {10,"hello",false}  --table

5.  a = print               --function

    使用type函数可以得到变量当前的类型,如print(type(a));
    
    nil         所有没有被赋值过的变量默认值为nil,给变量赋nil可以收回变量的空间。
    boolean     取值false和true。但要注意Lua中所有的值都可以作为条件。在控制结构的条件中除了false和nil为假,其他值都为真。所以Lua认为0和空串都是真。(注意,和C不一样哦)
    number      表示实数,Lua中没有整数。不用担心实数引起的误差,Lua的numbers可以处理任何长整数。
    string      字符串,Lua中的字符串可以存放任何包括0在内的二进制数据。可以使用单引号或双引号表示字符串,和C一样使用\作为转义符。也可以使用或 [[...]]表示字符串,它可以表示多行,而且不解释转义符(也可以是[=[...]=]、[==[]==]、...用于适应各种类型字符串)。另外要注意的是Lua中字符串是不可以修改的。
    function    函数,Lua中的函数也可以存储到变量中,可以作为其它函数的参数,可以作为函数的返回值。
    table       表,表是Lua特有的功能强大的东东,它是Lua中唯一的一种数据结构,它可以用来描述数组,结构,map的功能。
    userdata    userdata类型用来将任意C语言数据保存在Lua变量中。例如:用标准I/O库来描述文件。
    thread      线程。由coroutine表创建的一种数据类型,可以实现多线程协同操作。
   

3.表达式

    算术运行符: 加+、减-、乘*、除/、幂^
    关系运算符:小于<、大于>、小于等于<=、大于等于>=、等于==、不等~=
    逻辑运算符:与and、或or、非not
        and和or的运算结果返回值是其中的操作数:
        a and b        -- 如果a为false,则返回a,否则返回b
        a or  b        -- 如果a为true,则返回a,否则返回b
        所以C中的三元运算符a?b:c在Lua中可以这样写:(a and b) or c
    连接运算符:连续两个小数点..,如:
        "hello" .. "world"  结果是 "helloworld"
        0 .. 1              结果是 "01",当在一个数字后面写..时,必须加上空格以防止被解释错。
    取长度操作符:一元操作 #
        字符串的长度是它的字节数,table 的长度被定义成一个整数下标 n,它满足 t[n] 不是 nil 而 t[n+1] 为nil。
   

4.基本语法

赋值

    a = a + 1
    Lua里的赋值还可以同时给多个变量赋值。变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。如:
    a, b = 10, 2*x    --相当于a=10; b=2*x
    x, y = y, x        --交换x和y
    如果赋值符左右个数不同时,Lua会自动丢弃多余值或以nil补足

局部变量

    local i = 10
    使用local声明局部变量,局部变量只在所在的代码块内有效。
    如果不声明,默认为全局变量,这个变量在所有Lua环境中有效。
    代码块是指一个控制结构内,一个函数体,或者一个chunk(变量被声明的那个文件或者文本串),也可以直接使用do...end(相当于C中的{})。
条件

1.  if 条件 then

2.      then-part

3.  elseif 条件n then

4.      elseif-part

5.  ..                --->多个elseif

6.  else

7.      else-part

8.  end;

循环

    Lua中的循环有:while循环,repeat-until循环,for循环和for in循环。
    循环中可以用break跳出,Lua语法要求break和return只能是代码块的最后一句(放心,正常的代码都是满足这个要求的,break和 reuturn后面即使有代码也是执行不到的,再说了,大不了自己加个do...end好了^_^)
    如:

1.  local i = 1

2.  while a[i] do

3.      if a[i] == v then break end

4.      i = i + 1

5.  end

while循环

1.  while condition do

2.      statements;

3.  end;

repeat-until循环:

1.  repeat

2.      statements;

3.  until conditions;

for循环

1.  for var=exp1,exp2,exp3 do

2.      loop-part

3.  end

    for将用exp3作为step从exp1(初始值)到exp2(终止值),执行loop-part。其中exp3可以省略,默认step=1

for in循环

1.  for 变量 in 集合 do

2.      loop-part

3.  end

    实际上,
    for var_1, ..., var_n in explist do block end
    等价于

1.  do

2.      local _f, _s, _var = explist

3.      while true do

4.          local var_1, ... , var_n = _f(_s, _var)

5.          _var = var_1

6.          if _var == nil then break end

7.          block

8.      end

9.  end

    如:

1.  a = {"windows","macos","linux",n=3}

2.  for k,v in pairs(a) do print(k,v) end

5.函数

1.  function 函数名 (参数列表)

2.      statements-list;

3.  end;

    函数也可以一次返回多个值,如:

1.  function foo() return 'a','b','c'; end

2.  a,b,c = foo()


    在Lua中还可以直接定义匿名函数,如
    print((function() return 'a','b','c' end)())

你可能感兴趣的:(C++,lua,Visual)