--[[

루아글루 함수

루아글루 함수는 루아 스크립트에서 직접 호출할수 있는 형태로 작성된C++ 함수이다. 루아 스크립트에서 C++ 코드로의 통신에서 가장 주된 수단이 루아글루 함수이다.

루아 스크립트에서 C++로 작성된 함수가 필요할 때에는 루아글루 함수를 호출한다.

루아 프로그래머가 볼때에는 루아글루 함수와 루아 안에서 정의한 함수 사이에 아무차이가 없다.

이런 사실 덕분에, 필요한 함수를 일단 루아로 만들어서 사용하고 나중에 그 함수가 성능상의 병목임이 밝혀지면 C++로 이식하는 유용한 접근방식을 취할 수 있다.

진짜 루아 함수나 루아글루 함수나 루아 스크립트안에서 호출하는 방식은 정확히 동일 하므로, 원래의 진짜 루아 함수를 제거하고 (또는 이름을 바꾸고) 루아글루 함수로 대체한다고 해도 그함수를 사용하던 루아 스크립트는 변경할 필요가 없다.

루아글루 함수는 AddFunction메서드로 등록한다.(잠시 후에 예가 나온다.) LuaGlue라는 형식은 루아글루 함수의 서명을 간단하게 만들고 C++과의 연동 상의 문제점을 숨기기위한 사용자 정의 형식이다.

 

루아글루 함수: 매개변수와 반환값

루아글루 함수가 유용하게 쓰이려면, 루아 스크립트에서 함수에 전달한 매개변수들을 루아글루 함수에서 가져올 수 있어야 한다.  루아는 루아 스택을 통해서 매개변수들을 전달한다.  루아글루 함수의 매개변수 형식은 루아가 지원하는 어떠한 자료 형식이라도 가능하다.  루아의 모든 수치들은 C++에서 double이 되며, 문자열은 널 종료 문자열이 된다.   루아의 테이블 역시 함수의 인수가 될 수 있지만, 루아 API로 테이블을 다루는 것은 매우 번거로우며 곡 필요한 것도 아니다. 또한 루아는 사용자 자료라는 자료형식을 지원하며 이 형식 역시 루아글루 함수의 매개변수로 전달될 수 있지만, 이책에서는 다루지 않겠다.

cLua에는 루아글루 함수안에서 매개변수를 얻는데 사용할 수 있는 몇가지 메서드들이 있다. cLua::GetStringArgument는 문자열 인수에 해당하는 널 종료 문자열의 포인터를 돌려준다. 만일 주어진 인수가 문자열이 아니거나 어떤 문제가 있었다면 NULL을 돌려준다. cLua::GetNumberArgument는 double 형식의 수치 인수 값을 돌려주되, 문제가 있었다면 0.0을 돌려준다.  두 메서드 모두, 루아 스택에서 가져올 매개변수의 번호를 지정하는 매개변수를 받는다. 매개변수 번호는 호출문의 매개변수들에서 제일 왼쪽 매개변수가 1이고 오른쪽으로 가면서 1씩증가한다.

반환값 역시 루아 스택을 거친다. 반환값을 돌려주는 방식은 간단하다. 호출자에게 돌려줄 값을 루아 스택에 쌓고, 그 개수를 C++ return 문을 통해서 루아에게 알려주면 된다. C++함수가 루아 환경에게 반환값 개수를 돌려주는 이유는, 루아에서 하나의 함수가 여러개의 반환값들을 돌려줄 수있기때문이다.  이러한 다중 반환값은 매우 유용하게 쓰이며 루아글루 함수에도 적용된다.  반환값 개수와 루아 스택에 쌓은 값들의 개수는 일치해야 한다. 루아 스택에 값을 쌓을때 사용하는  cLua 메서드는 cLua::PushString과 cLua::PushNumber이다.

아래에 루아 글루 함수의 예가 나와있다. 이 예에서 보듯이 매개변수 번호에 해당하는 지역 변수를 1로 초기화하고, 각 매개 변수를 가져올때마다 후증가시키면 매개변수 번호가 잘못될 위험이 없다.

그리고 함수 처리 결과에 따라 의미 있는 반환값개수가 달라진다고 하더라도, 기본값들을 채워서라도 항상 같은 개수의 반환값들을 돌려 주는것이 좋다. 루아에서 nil에 접근하면 오류가 발생하기 때문이다.  오류처리에 대해서는 나중에 다시 이야기 하겠다.

--]]

--루아글루 함수에서 매개변수들을 얻고 반환값을 돌려주는 예

extern cLua *pLua;

LuaGlue _SwapExt(lua_State *L)

{

  int argNum = 1;

  const char *fileName = pLua->getStringArgument(argNum++);

  const char *newExt =

     pLua->GetStringArgument(argNum++);

  char fname[_MAX_FNAME];

  char ext[_MAX_EXT];

  _splitpath(fileName, NULL, NULL, fname, ext);

  std::string sRet = fname;

  if(newExt[0] != '\0')

  {

     sRet += ".";

     sRet += newExt;

   }

  lua->PushString(sRet.c_str());

  return 1;

}

--[[

이 함수는 cLua::GetStringArgument 를 이용해서 루아 환경으로부터 두 개의 문자열 인수를 가져온다.(일단 C++로 넘어오면 그 문자열들은 널 종료 문자열이 된다.)그 문자열들로 약간의 조작을 한 후, 결과 문자열을 cLua::PushString 으로 루아 스택에 쌓고 1(반환값 개수)을 루아에게 돌려준다

 

요약

루아의 설계자들은 루아를 매우 유연하고 이식성 좋은 방식으로 C++ 프로그램에 통합 시킬수 있는 시스템을 만들어 두었다 이번 장에서는 여러 가지 예제들을 통해서 C++프로그램에 루아를 통합하는 방법을 살펴보았으며, 또 그러한 작업에 관련된 복잡하고 읽기 어려운 코드를 좀 더 읽기 쉽고 깔끔하게 만들어주는 C++ 클래스도 하나 소개했다.

--]]

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

-- 일단은 책에 있는 내용을 거의 그대로 타이핑했습니다..-_-;  이 부분때문에 이 책을 구입했다고 -- 해도 과언이 아니기때문에....;; 통합부분과 연동부분만 책의 내용으로 타이핑예정입니다..

 

--[[

루아 배포판을 하나의 라이브러리로 컴파일 해두면 프로젝트가 좀 더 깔끔해진다.

루아 소스코드를 응용프로그램과 분리할 수있으므로 루아 소스 코드를 갱신 수정한다고 해도 응용 프로그램을 다시 컴파일 할 필요가 없으며 마찬가지로 응용 프로그램을 완전히 다시 빌드한다고 하더라도 루아 소스 코드는 다시 컴파일할 필요가 없다.  더 나아가서, 대부분의 프로젝트는 루아 API기능성의 일부만을 사용하는데 그런 기능성을 클래스로 추상화 한다면 루아의 초기화나 종료, 연동에 관련된 번잡한 부분을 숨길수 있다.  여기서 소개할 cLua 클래스가 그런의도로 만들어진 것이다.  밑은 cLua 클래스의 정의이다.

--]]

--cLua헤더

 

struct lua_State;

#define LuaGlue extern "C" int
extern "C" {
typedef int (*LuaFunctionType)(struct lua_State *pLuaState);
};

class cLua
{
public:
 cLua();
 virtual ~cLua();

 bool  RunScript(const char *pFilename);
 bool  RunString(const char *pCommand);
 const char *GetErrorString(void);
 bool  AddFunction(const char *pFunctionName, LuaFunctionType pFunction);
 const char *GetStringArgument(int num, const char *pDefault=NULL);
 double  GetNumberArgument(int num, double dDefault=0.0);
 void  PushString(const char *pString);
 void  PushNumber(double value);

 void  SetErrorHandler(void(*pErrHandler)(const char *pError)) {m_pErrorHandler = pErrHandler;}

 lua_State *GetScriptContext(void)  {return m_pScriptContext;}

private:
 lua_State *m_pScriptContext;
 void(*m_pErrorHandler)(const char *pError);
};
--[[

생성자는 루아 환경을 열고 소멸자는 그것을 닫는다.  메서드 RunString 과 RunScript는 각각 루아 스크립트 코드를 담은 문자열 또는 파일을 실행한다.   그 외의 메서드들은 루아글루 함수를 정의하거나, 루아글루 함수에 주어진 인수들을 얻거나, 루아 글루 함수에서 루아로 반환값을 돌려주는 작업을 좀더 편하게 수행하기 위한것들이다.

밑은 cLua클래스를 이용하도록 갱신된 콘솔프로그램이다.  코드가 훨씬작고 간단해졌음을 알 수 있다.

--]]

//갱신된 콘솔 프로그램..

//제한된 기능을 가진 간단한 루아 콘솔

//cLua 클래스와 루아 라이브러리를 사용한다.

//표준 헤더들을 포함한다.
#include <stdio.h>
#include <string.h>
#include <cLua.h>

LuaGlue _Version(lua_State *L)
{
 puts("This is Version 2.0 of the Console Program");
 return 0;
}

char gpCommandBuffer[254];

const char *GetCommand(void)
{
 printf("Ready> ");
 return gets(gpCommandBuffer);
 puts("\n");
}

void main(void)
{
//프로그램정보와 간단한 사용법을 출력
 puts("Lua Console (c) 2004 Charles River Media");
 puts("Enter Lua commands at the prompt, [QUIT] to exit\n\n");

 cLua *pLua = new cLua;

 pLua->AddFunction("Version", _Version);

//명령문을 반복적으로 처리한다.
 const char *pCommand = GetCommand();
 while(stricmp(pCommand, "QUIT") != 0)
 {
  // 문자열을 cLua로 전달한다.

  if(!pLua->RunString(pCommand))
  {
   printf("ERROR:%s\n", pLua->GetErrorString());
  }

  // 다음 명령문을 얻는다.

  pCommand = GetCommand();
 }
 
 delete pLua;
}

--[[
이제는 루아헤더들을 포함하지 않아도 된다. cLua.h만 포함하면 된다.  그리고 루아 글루함수 _Version의 서명이 LuaGlue 형식 덕분에 짧아졌다.  루아 환경을 열고 닫는 작업은 cLua 객체의 생성자와 소멸자에 의해 자동적으로 처리된다.  객체를 생성할때 루아 환경이 열리고 파괴할때 환경이 닫힌다. cLua::AddFunction은 루아 글루 함수Version을 추가한다.  등록할 함수가 많은 경우에는 원래의 예제처럼 자료구조와 루프를 이용하는것이 편할것이다.  cLua::Runstring은 문자열을 루아로 적재하고 실행한다. 루아와의 연동에 관련된 다소복잡한 과정을 이런 메서드들 안에 숨겨둠으로써, 최종적인 코드가 매우 깔끔하고 읽기 쉬워졌다.

--]]

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

//스크립트 언어를 다뤄본적이 없어서 개념 잡기가 힘들었습니다..(-_-;;  아이큐가 낮아요..)

//개인적으로 가장 이해하기 힘들었던(남들은 다 아는듯 하지만..)통합하는 과정입니다..

//CD에 있는 소스를 따로 분리해서 올려놨습니다....문제가 있을지도 모르겠네요..;

 

 

//<루아를 이용한 민첩하고 효과적인 게임 개발 >

 

// 제한된 기능을 가진 간단한 루아 콘솔

// 시스템 헤더들을 포함한다.
#include <stdio.h>
#include <string.h>

/*
** 루아 헤더들을 포함한다.

** 루아 헤더들은 C로 되어있다. 그런데 이것은 C++프로그램이기 때문에,

** 루아 헤더들에 담긴 정의들이 C로 된것이고 따라서 C++ 이름섞기를

** 적용하지 말아야 한다는 점을 C++ 컴파일러에게 알려주어야한다

** extern "C"가 바로 그런 의미이다.

*/
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

/*
** 루아글루 함수의 예이다.

** 이 함수는 루아에서 호출한다.

** 루아는 ANSI C로 작성된 것이므로, 앞에서와 마찬가지로

** extern "C"를 지정해서 C++이름 섞기를 방지한다.
*/

extern "C" int _Version(lua_State *L)
{
 puts("This is Version 1.0 of the Console Program");
 puts(LUA_VERSION);
 puts(LUA_COPYRIGHT);
 puts(LUA_AUTHORS);
 return 0;
}

/*
** 루아 환경에 등록할 루아 글루 함수들의 정보

** 이후 루아 API를 이용해서 이들을

** 실제로 루아 환경에 등록한다.
*/

static luaL_reg ConsoleGlue[] =
{
 {"Version",  _Version},
 {NULL, NULL}
};

char gpCommandBuffer[254];

const char *GetCommand(void)
{
 printf("Ready> ");
 return gets(gpCommandBuffer);
 puts("\n");
}

/* 루아 환경생성

  가장 먼저 할일은 루아 환경을 만드는 것이다 이때 사용하는 루아 API 함수는 lua_open()이다.

*/

void main(void)
{
 lua_State *pLuaState = lua_open();
 luaopen_base(pLuaState);
 luaopen_io(pLuaState);
 luaopen_string(pLuaState);
 luaopen_math(pLuaState);
 luaopen_debug(pLuaState);
 luaopen_table(pLuaState);

/* lua_open()함수는 생성된 루아 환경을 뜻하는 lua_State구조체의 포인터를 돌려준다.

 이후 사용할 대부분의 루아 API함수들은 이포인터를 첫번재 인수로 받는다.

lua_State를 만든 다음에는 응용프로그램에 필요한 루아 표준 라이브러리들을 초기화 한다.

이 예제의 경우에는 루아 5.0.2배포판에 있는 모든 라이브러리를 초기화했다

이렇게 하면 루아 스크립트는 루아의 모든 함수들을 사용할 수 있다. 응용 프로그램에 따라서는 일부 라이브러리가 필요 없을수도 있는데, 그런 경우라면 해당 라이브러리를 여는 함수 호출을 생략하면 된다.

루아 글루 추가

다음으로 루아 스크립트에서 C++코드에 접근하기 위한 루아글루 함수들을 등록한다.

바로밑에 간단한 자료구조와 루프문을 이용해서 여러개의 루아글루 함수들을 등록하는 예가 나와있다.  지금 예제의 경우 콘솔 프로그램의 버전과 루아 배포판 자체의 버전을 출력하는 간단한 함수를 루아환경에 등록한다.

*/

//루아 글루 등록
 for(int i=0; ConsoleGlue[i].name; i++)
 {
  lua_register(pLuaState, ConsoleGlue[i].name, ConsoleGlue[i].func);
 }

/* 콘솔명령 처리

이제 키보드로 문자열을 받고 그것을 루아 환경에 넘기는 과정을 반복하는 루프문만 작성하면 콘솔이 완성된다 밑의 루아API함수 luaL_loadbuffer는 주어진 문자열(루아 청크)을 루아 환경안으로

적재하고, 구문을 점검하고, 실행 가능한 형태로 준비한다. 코드가 모든 점검을 통과하고 그래서 실행 준비가 되었으면 함수는 0 을 돌려준다. 그런후에는 루아 API함수 lua_pcall로 그것을 실행한다.

실행이 성공적이라면 이 함수는 0을 돌려준다. 오류가 있었다면, 루아는 그 오류를 설명하는 문자열 하나를 루아 스택에 넣는다.  그 문자열은 루아API함수 luaL_checkstring 으로 얻을수있다.

예제 콘솔 프로그램은 ERROR:다음에 루아가 돌려준 문자열을 출력한다.

*/
 // 콘솔 처리루프
 const char *pCommand = GetCommand();
 while(stricmp(pCommand, "QUIT") != 0)
 {
  // 사용자가 입력한 명령문을 루아 환경에 보낸다.

  if (luaL_loadbuffer(pLuaState, pCommand, strlen(pCommand), NULL) == 0)
  {
   if(lua_pcall(pLuaState, 0, LUA_MULTRET, 0) != 0)
   {
    // 명령 실행 실패 - 오류 메시지를 출력

    printf("ERROR:%s\n", luaL_checkstring(pLuaState, -1));
   }
  }
  else
  {
   // 명령을 적재하는 과정에서 오류가 있었음

   printf("ERROR:%s\n", luaL_checkstring(pLuaState, -1));
  }

  // 다음 명령문을 얻는다.

  pCommand = GetCommand();
 }

 lua_close(pLuaState);
}
/* 응용프로그램 종료

사용자가 QUIT 을 입력하면 while 루프가 끝난다.  그러면 다음과 같은 코드로 lua_State를 해제한다.  lua_close(pLuaState);

이상의 예제는 C++ 프로그램에서 루아를 실행하는 최소한의 형태를 보여준다.

이 예제에서는 그냥 모든 관련 소스 코드를 Visual C++ 프로젝트에 직접 포함하고 루아 API를 직접 호출했는데, 작은 프로젝트라면 문제가 없지만, 좀 더 큰 프로젝트들을 위해서는 루아 소스 코드들을 하나의 라이브러리로 만들어 두고, 또 루아 API 관련 작업을 C++클래스들로 포장해서 재사용하는것이 좋을것이다.

*/

 

 

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

출처 : http://www.codegurus.be/codegurus/Programming/luahooks_en.htm

 

How to prevent LUA scripts that block your program

Introduction

In the previous two articles about LUA we learned how to embed LUA in C/C++.  But a nasty script can also block the C/C++ application by creating an endless loop.  An example of such a terrible script would be this :

If you execute such a script then your application will hang forever.  Luckily LUA provides hooks and Windows provides threads.  And for people who can't wait : the download is at the bottom of the page :-).

 

Threads

I'm not talking about LUA threads here, but about the threads of your operating system (Win32 in our case).  In order to make the script execution independent of your interface you have to create two threads.  One for the interface and one for the script execution.  If now a bad script will block the program it will only block the executioner thread and not the interface thread.

VERY IMPORTANT NOTE: the code below is only to show how to use a thread to avoid blocking of your program, it doesn't tell how to make LUA thread-safe/multithreaded!  Click on this link to read on how to make LUA multithreaded and avoid crashes.

To enable threads in your program you have to include process.h and you have to tell the compiler you're going to create a multithreaded program, go to the Project Settings, go to the C/C++ tab and select "Code Generation" as the category.

For the debug version select "Debug multithreaded" :

For the release version select "Multithreaded" :

In your program you can start a thread by calling the C library function _beginthread.  For example :

 

LUA Hooks/Events

Hooks/events are mainly used for debugging your LUA state but we can also use it to abort a running script.  We are interested in a hook/event called "The line hook".  This event will occur every time an instruction is about to be executed.  To set a hook we must call the lua_sethook function, it's defined as follows :

LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);

There are as you can see 4 parameters, the first one is (like always) the pointer to the LUA state, the second one is the address of a C function that handles the event.  The third parameter tells us for which kind of events the hook function will be called, the fourth parameter is only used for the LUA_HOOKCOUNT hook, see the manual.

We first create a global variable to tell us wether or not to abort the script.

Then we create the following short but very effective hook/event handler :

We will register the hook/event handler with lua_sethook as follows :

For more information about debugging and other types of hooks please refer to your LUA manual.

 

Modify your functions to protect against blocking scripts

Let's assume we create a script function sleep, the function will call the Windows Sleep function (this function suspends execution for X milliseconds).  We define this glue function as follows :

By implementing the function as such you have just given a bad LUA scripter the power to make your application hang for a long time.  The problem is this : if the scripter calls the sleep function with a high value, let's say : sleep(1500000) then your program will hang for 1500 seconds or 25 minutes!

A better implementation would be to sleep only for short moments and to check wether or not the script has been aborted, like this :

In the sleep function as above, the sleep is executed for a short duration of 100ms.  So if we want to suspend execution for 5 seconds (5000 milliseconds), then the Sleep function will be called 50 times, so every 50 times we check wether to abort the script/sleep function or not.

 

Event Objects

The sleep function defined above isn't that optimal, the program is executing a continuous loop eating CPU time.  Windows provides a better technique to avoid this: waitable event objects.  An event object allows threads to notify each other that some kind of an event has occured.  Windows has a function WaitForSingleObject it acts like Sleep but it will abort the function if the state of an event object is signaled (signaled means that the event has occured).

We create a global handle varible hCloseProgram to represent the event :

In the start-up of your program you create the event object using the CreateEvent function :

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttribute, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);

There are four parameters.  The first one represents the security attributes, we simple use NULL, the second one tells us we will control the event manually, the 3th parameter will be FALSE and that means that the initial state of the events is non-signaled, and the last parameter is a user-defined name (which can be NULL to create a nameless event) :

Our new glue function becomes this :

And in order to cancel the WaitForSingleObject, all you do is :

Of course, event objects still don't abort the LUA script executioner.  You also need to modify your hook function to teach it how to use the event object :

But the be honest, that's not a method I would recommend, the hook routine will be called a lot and calling every time the WaitForSingleObject function causes it to slow down and thus slowing down your scripts!  A better method would be a combination of both a global variable to allow aborting of the script engine and an event to allow aborting of functions that suspend the system.  See the LUA Demo 3 program to see how it is done.

I also invite you to experiment with another method : you can for example set only the LUA hook routine when you need to close your program.  For example :

 

About LUA Demo 3

This demo project is a bit more complicated than the other LUA downloadable examples.  It's an application that allows you to enter an LUA script that will be executed, either using a seperate thread or using only the main thread.  If no seperate thread is used the program will exit at the end of the script execution.  And in case of a seperate thread, the main thread will be suspended indefinitly (INFINITE) (until hCloseProgram becomes signaled).  This little test project is also safe against blocking scripts.  It includes most of the programming techniques explained above, feel free to browse and use the source code.  The program will also warn the user if the LUA script took too long to shut down (which normally shouldn't happen).

It's a nice LUA demonstration project, the following LUA functions are created :

clrscr( ) = clear the screen
getx( ) = get the current x coordinate
gety( ) = get the current y coordinate
gotoxy( ) = change the cursor position
printf( ) = print text
kbhit( ) = has a key been hit?
delay( ) = the sleep function (but in traditional conio.h style)
exit ( ) = exit the script

The program also allows you to save & load scripts, have you made a nice script with it?  Please e-mail it to me and show me what you've created with my litle demo program.  Thanks.

 

Download

You can download the above test project here (compressed ZIP files) :

The source code of LUA Demo 3
The executable version of LUA Demo 3

NOTE: All the required files are included in the source code file in order to compile succesfully

IMPORTANT: Even though the download contains multithreaded code, the LUA state isn't thread-safe in this example. Full LUA multithreading & thread-safety is discussed in this article (follow the link)!

 

Contact

If you have questions or remarks about this article or its contents, then feel free to contact me at <fibergeek @ codegurus.be>.  Don't forget to remove the white spaces or the e-mail won't arrive.

 

External links

LUA: the official site

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,