代码共享如下,在Win2K sp4/WinXP sp2上调试通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
BOOL CheckFileTrust( LPCWSTR lpFileName )
{
BOOL bRet = FALSE;
WINTRUST_DATA wd = { 0 };
WINTRUST_FILE_INFO wfi = { 0 };
WINTRUST_CATALOG_INFO wci = { 0 };
CATALOG_INFO ci = { 0 };

HCATADMIN hCatAdmin = NULL;
<FONT color=blue>if</FONT> ( !CryptCATAdminAcquireContext( &amp;hCatAdmin, NULL, 0 ) )
{
<FONT color=blue>return</FONT> FALSE;
}

HANDLE hFile = CreateFileW( lpFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL );
<FONT color=blue>if</FONT> ( INVALID_HANDLE_VALUE == hFile )
{
CryptCATAdminReleaseContext( hCatAdmin, 0 );
<FONT color=blue>return</FONT> FALSE;
}

DWORD dwCnt = 100;
BYTE byHash[100];
CryptCATAdminCalcHashFromFileHandle( hFile, &amp;dwCnt, byHash, 0 );
CloseHandle( hFile );

LPWSTR pszMemberTag = <FONT color=blue>new</FONT> WCHAR[dwCnt * 2 + 1];
<FONT color=blue>for</FONT> ( DWORD dw = 0; dw &lt; dwCnt; ++dw )
{
wsprintfW( &amp;pszMemberTag[dw * 2], L"%02X", byHash[dw] );
}

HCATINFO hCatInfo = CryptCATAdminEnumCatalogFromHash( hCatAdmin,
byHash, dwCnt, 0, NULL );
<FONT color=blue>if</FONT> ( NULL == hCatInfo )
{
wfi.cbStruct = <FONT color=blue>sizeof</FONT>( WINTRUST_FILE_INFO );
wfi.pcwszFilePath = lpFileName;
wfi.hFile = NULL;
wfi.pgKnownSubject = NULL;

wd.cbStruct = <FONT color=blue>sizeof</FONT>( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &amp;wfi;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwStateAction = WTD_STATEACTION_IGNORE;
wd.dwProvFlags = WTD_SAFER_FLAG;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
}
<FONT color=blue>else</FONT>
{
CryptCATCatalogInfoFromContext( hCatInfo, &amp;ci, 0 );
wci.cbStruct = <FONT color=blue>sizeof</FONT>( WINTRUST_CATALOG_INFO );
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = lpFileName;
wci.pcwszMemberTag = pszMemberTag;

wd.cbStruct = <FONT color=blue>sizeof</FONT>( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &amp;wci;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
wd.dwProvFlags = 0;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
}
GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;
HRESULT hr = WinVerifyTrust( NULL, &amp;action, &amp;wd );
bRet = SUCCEEDED( hr );

<FONT color=blue>if</FONT> ( NULL != hCatInfo )
{
CryptCATAdminReleaseCatalogContext( hCatAdmin, hCatInfo, 0 );
}
CryptCATAdminReleaseContext( hCatAdmin, 0 ); <FONT color=green>// 2007.4.10感谢童志明君指出一处内存泄漏</FONT>
<FONT color=blue>delete</FONT>[] pszMemberTag;
<FONT color=blue>return</FONT> bRet;
}

这段代码是在一个老外的论坛上不经意搜索到的,一个貌似德国人(因为他的注释不是英文写的,德国亦仅猜测尔,西班牙、葡萄牙、法兰西、俄罗斯亦都有可能)写的Delphi代码,其中使用了WinTrust.dll中的导出函数。使用VS2005的朋友们可以包含WinTrust.h、SoftPub.h和Mscat.h,并添加导入库WinTrust.lib;使用VC6的朋友们可以参考MSDN上的函数及结构体声明,并用函数指针进行调用。

我要做自己情绪的主人!

潮起潮落、冬去春来、月圆月缺、花飞花谢,自然界万物都在循环往复的变化之中,我也不例外,情绪会时好时坏。每天清晨醒来时,我可能不再有旧日的情怀。昨日的欢乐变成今日的哀愁,今日的悲伤又转为明日的喜悦。我的心像车轮般不停地旋转:由乐而悲,由悲而喜,由喜而忧。这就好比花儿的变化,今日绽放时的喜悦终将变成明天凋谢时的绝望。但是我要牢牢记住:正如今天枯败的花儿蕴藏着明天新生的种子,今天的悲伤也预示着明日的欢乐。

我要做自己情绪的主人!

我怎样才能控制自己的情绪,使我的每一天卓有成效?我只知道这样一个事实:无论我为他人带来的是风雨、忧郁、黑暗和悲观,还是欢乐、喜悦、光明和笑声,他们都会以同样而报之。花草树木,随着气候的变化而生长,而我要为自己创造天气,学会用自己的心灵弥补气候的不足。我必须得心平气和,否则迎来的又将是失败的一天。

我要做自己情绪的主人!

我怎样才能控制自己的情绪,让我的每一天充满幸福和欢乐?我要学会这个千古秘诀:弱者任思绪控制行为,强者让行为控制思绪。每天醒来当我被悲伤、自怜、失败的情绪包围时,我就这样与之对抗:沮丧时,我引亢高歌;悲伤时,我开怀大笑;病痛时,我加倍工作;恐惧时,我勇往直前;自卑时,我换上新装;不安时,我提高嗓音;穷困潦倒时,我想象未来的富有;力不从心时,我回想过去的成功;自轻自贱时,我想想自己的目标。

我要做自己情绪的主人!

从今往后,我必须不断对抗那些企图摧垮我的力量。失望与悲伤一眼就可识破,而那些不易觉察、面带微笑、招手而来的敌人,却随时可能将我摧毁。对它们,我永远不能放松警惕:自高自大时,我要追寻失败的记忆;洋洋得意时,我要想想竞争的对手;沾沾自喜时,不要忘了那忍辱的时刻;自以为是时,看看自己能否让风止步;骄傲自满时,要想到自己怯懦的时候;不可一世时,抬起头来,仰望天上群星。

我要做自己情绪的主人!

有了这项本领,我就能体察别人的情绪变化。我会宽容怒气冲冲的人,因为他尚未懂得控制自己的情绪;我能忍受他人的指责与辱骂,因为知道明天他会重新变得随和。我不再只凭一面之交来判断他人,也不再因一时的怨恨与人绝交。今天不肯花一分钱购买金蓬马车的人,明天也许会用全部家当换取树苗。知道了这个秘密,我对人可以宽容大度,对事能够冷静客观。

我要做自己情绪的主人!

我从此领悟了人类情绪变化的奥秘。对于自己千变万化的个性,我不再听之任之,我知道,只有积极主动地控制情绪,才能掌握自己的命运。我成为自己情绪的主人,我由此而变得伟大。

我要做自己情绪的主人!

年初的时候,几乎天天跟人吵架,因为什么呢?因为自己的狗脾气,那会甚至都不管自己到底对不对,反正就是吵。坦诚的说,后来都有点因此而沾沾自喜,觉得那是自己NB,自己是个有个性的人。事到如今,真是忍不住要笑一笑自己了,呵呵,用下面文章里的一句话来说就是:弱 者任情绪控制行为,强者让行为控制情绪。我是弱者,所以让情绪控制了自己的行为,还引以为荣。呵呵,你说可笑不?可笑。把这篇文章收藏在这里,供自己今后 再次翻阅。我想,只要在今后狗脾气再犯的时候,能来这里看看,确实能认真的读一下下面的文字,那我一定可以从弱者变成一个可以控制情绪的人的。

 

Today I will be master of my emotions.
The tides advance; the tides recede. Winter goes and summer comes. summer wanes and the cold increases. The sun rises; the sun sets. The moon is full; the moon is black. The birds arrive;the birds depart. Flowers bloom; flowers fade.Seeds are sown; harvests are reaped. all nature is a circle of moods and I am a part of nature and so,like the tides, my moods will rise; my moods will fall.

今天我学会控制情绪。  
潮起潮落,冬去春来,夏末秋至,日出日落,月圆月缺,雁来雁往,花飞花谢,草长瓜熟,自然界万物都在循环往复的变化中,我也不例外,情绪会时好时坏。

Today I will be master of my emotions.
It is one of nature's tricks, little understood,that each day I awaken with moods that have changed from yesterday. Yesterday's joy wilI become today's sadness; yet today's sadness will grow into tomorrow's joy. Inside me is a wheel,constantly turning from sadness to joy, from exultation to depression, from happiness to melancholy. Like the flowers, today's full bloom of joy will fade and wither into despondency, yet I will remember that as today's dead flower carries the seed of tomorrow's bloom so, too, does today's sadness carry the seed of tomorrow's joy.

今天我学会控制情绪。
这是大自然的玩笑,很少有人窥破天机。每天我醒来时,不再有旧日的心情。昨日的快乐变成今天的哀愁,今天的悲伤又转为明日的喜悦。我心中像一只轮子不停地转着,由乐而悲,由悲而喜,由喜而忧。这就好比花儿的变化,今天枯败的花儿蕴藏着明天新生的种子,今天的悲伤也预示着明天的快乐。


Today I will be master of my emotions.
And how will I master these emotions so that each day will be productive? For unless my mood is right the day will be a failure. Trees and plants depend on the weather to flourish but I make my own weather, yea I transport it with me. If I bring rain and gloom and darkness and pessimism to my customers then they will react with rain and gloom and darkness and pessimism and they will purchase naught. If I bring joy and enthusiasm and brightness and laughter to my customers they will react with joy and enthusiasm and brightness and laughter and my weather will produce a harvest of sales and a granary of gold for me.
      

       今天我学会控制情绪。我怎样才能控制情绪,以使每天卓有成效呢?除非我心平气和,否则迎来的又将是失败的一天。花草树木,随着气候的变化而生长,但是我为自己创造天气。我要学会用自己的心灵弥补气候的不足。如果我为顾客带来风雨、忧郁、黑暗和悲观,那么他们也会报之于风雨、忧郁、黑暗和悲观,而他们什么也不会买。相反的,如果我们为顾客献上欢乐、喜悦、光明和笑声,他们也会报之以欢乐、喜悦、光明和笑声,我就能获得销售上的丰收,赚取成仓的金币。

Today I will be master of my emotions.
And how will I master my emotions so that every day is a happy day, and a productive one? I will learn this secret of the ages: Weak is he who permits his thoughts to control his actions; strong is he who forces his actions to control his thoughts. Each day, when I awaken, I will follow this plan of battle before I am captured by the forces of sadness, self-pity and failure-

今天我学会控制情绪。
我怎样才能控制情绪,让每天充满幸福和欢乐?我要学会这个千古秘诀:弱者任情绪控制行为,强者让行为控制情绪。每天醒来当我被悲伤、自怜、失败的情绪包围时,我就这样与之对抗:

If I feel depressed I will sing.
If I feel sad I will laugh.
If I feel ill I will double my labor.
If I feel fear I will plunge ahead.
If I feel inferior I will wear new garments.
If I feel uncertain I will raise my voice.
If I feel poverty I will think of wealth to come.
If I feel incompetent I will remember past success.
If I feel insignificant I will remember my goals.

沮丧时,我引吭高歌。
悲伤时,我开怀大笑。
病痛时,我加倍工作。
恐惧时,我勇往直前。
自卑时,我换上新装。
不安时,我提高嗓声。
穷困潦倒时,我想象未来的富有。
力不从心时,我回想过去的成功。
自轻自贱时,我想想自己的目标。

Today I will be master of my emotions.
Henceforth, I will know that only those with inferior ability can always be at their best, and I am not inferior. There will be days When I must IN THE WORLD constantly struggle against forces which would tear me down. Those such as despair and sadness are simple to recognize but there are others which approach with a smile and the hand of friendship and they can also destroy me. Against them, too, I must never relinquish control-

总之,今天我要学会控制自己的情绪。
从今往后,我明白了,只有低能者才会江郎才尽,我并非低能者,我必须不断对抗那限些企图摧垮我的力量。失望与悲伤一眼就会被识破,而其它许多敌人是不易觉察的。它们往往面带微笑,却随时可能将我们摧垮。对它们,我们永远不能放松警惕。 自高自大时,我要追寻失败的记忆。

If I become overconfident I will recall my failures.
If I overindulge I will think of past hungers.
If I feel complacency I will remember my competition.
If I enjoy moments of greatness I will remember moments of shame.
If I feel all-powerful I will try to stop the wind.
lf I attain great wealth I will remember one unfed mouth.
If l become overly proud I will remember a moment of weakness.
If I feel my skill is unmatched I will look at the stars.

纵情得意时,我要记得挨饿的日子。
洋洋得意时,我要想想竞争的对手。
沾沾自喜时,不要忘了那忍辱的时刻。
自以为是时,看看自己能否让步驻步。
腰缠万贯时,想想那些食不果腹的人。
骄傲自满时,要想到自己怯懦的时候。
不可一世时,让我抬头,仰望群星。

Today I will be master of my emotions.
And with this new knowledge I will also understand and recognize the moods of him on whom I call. I will make allowances for his anger and irritation of today for he knows not the secret of controlling his mind. I can withstand his arrows and insults for now I know that tomorrow he will change and be a joy to approach.
No longer will l judge a man on one meeting;no longer will I fail to call again tomorrow on he who meets me with hate today. This day he will not buy gold chariots for a penny, yet tomorrow he would exchange his home for a tree. My knowledge of this secret will be my key to great wealth.

今天我学会控制情绪。
有了这项新本领,我也更能体察别人的情绪变化。我宽容怒气冲冲的人,因为他尚未懂得控制自己的情绪,就可以忍受他的指责与辱骂,因为我知道明天他会改变,重新变得随和。我不再只凭一面之交来判断一个人,也不再一时的怨恨与人绝交,今天不肯花一分钱买金蓬马车的人,明天也许会用全部家当换树苗。知道了这个秘密,我可以获得极大的财富。

Today l will be master of my emotions.
Henceforth I will recognize and identify the mystery of moods in all mankind, and in me.From this moment I am prepared to control whatever personality awakes in me each day. I will master my moods through positive action and when I master my moods I will control my destiny.
Today I control my destiny, and my destiny is to become the greatest salesman in the world!
I will become master of myself.
I wi1l become great.



今天我学会控制情绪。
我从此领悟人类情绪的变化的奥秘。对于自己千变万化的个性,我不再听之任之,我知道,只有积极主动地控制情绪,才能掌握自己的命运。
我控制自己的命运就是成为世界上最伟大的推销员!
我成为自己的主人。
我由此而变得伟大。

【肇法师曰】闻说诸心。谓有实心。故须破遣。明三世皆空。故论云。过去已灭。未来未起。现在虚妄。三世推求。了不可得。

      【疏钞云】未觉不知。随时流转。故有三世。若悟真一之心。即无过去现在未来。若有过去心可灭。即是自灭。若有未来心可生。即是自生。既有生有灭。即非常住真心。即为依他心。虚妄心。若一念有生灭心。即成六十二种邪见。九百种烦恼。

      【王日休曰】常住真心。即真性也。是以自无量无数劫来。常一定而不变动。岂有过去未来现在哉。若有过去未来现在。则为妄想。此三心是也。且若饱而未欲食。则饮食之心为未来。饥而正欲食。则欲食之心为现在。食毕而放匕箸。则欲食之心为过去。是此心因事而起。事过而灭。故为妄想也。不可得者。谓无也。言此三心本来无有。乃因事而有耳。圆觉经所以言六尘缘影。为自心相者。谓众生以六种尘缘之影。为自己之心相也。

      【僧若讷曰】本生心地观经云。如佛所说。唯将心法。为三界主。心法本源。不染尘秽。云何心法。染贪嗔痴。于三世法。唯说为心。过去心已灭。未来心未至。现在心不住。诸法之内性不可得。诸法之外相不可得。诸法中间都不可得。心法本来无有形相。心法本来无有住处。一切如来尚不可见心。何况余人。得见心法。

      【颜丙曰】谓思念前事者。为过去心。思念今事者。为现在心。思念后事者。为未来心。三念总放下者。谓之不可得。经云。前念后念及今念。念念不被邪见染。此为三心不可得。古云。一念不生全体现。亦谓三际俱断。三念俱妄。了不可得。

      【傅大士颂曰】依他一念起。俱为妄所行。便分六十二。九百乱纵横。(法华经二卷。世尊偈言。薄德少福人。众苦所逼迫。入邪见稠林。若有若无等。依止此诸见。具足六十二。毗婆沙论云。六十二见者。五蕴中各起四见。四五二十。三世各二十。通为六十。断常二见为根本。总为六十二见。且于色蕴中。即色是我。离色非我。我中有色。色中有我。五蕴中具有此四。疏钞解三心云。若一念有生灭心。即成六十二种邪见。九百种烦恼。)过此灭无灭。(一作不灭。)当来生不生。常能作此观。(去声)真妄坦然平。(晁文元公遇高士刘惟一。访以生灭之事。刘曰。人常不死。公骇之。刘曰。形死性不灭。是知此性历长存。)

      【川禅师曰】低声低声。真得鼻孔里出气。颂曰。三际求心心不见。两眼依然对两眼。不须遗剑刻舟寻。雪月风花常见面。

      【未曾有经云】妙吉祥菩萨。因见一人。言我造杀业。决堕地狱。如何救度。菩萨即化一人。亦曰。我造杀业。决堕地狱。前人闻已。言我亦然。化人告之。唯佛能救。相随共诣。化人白佛。我造杀业。怖堕地狱。愿佛救度。佛即告言。如汝所说造杀业者。汝从何心而起业相。为过去耶。未来耶。现在耶。若起过去心者。过去已灭。心不可得。若起未来心者。未来未来。心不可得。若起见在心者。见在不住。心不可得。三界俱不可得故。即无起作。无起作故。于其罪相何所见耶。善男子。心无所住。不在内外中间。心无色相。非青黄赤白。心无造作。无作者故。心非幻化。本真实故。心无边际。非限量故。心无取舍。非善恶故。心无转动。非生灭故。心等空虚。无障碍故。心非染净。离一切数故。善男子作是观者。即于一切法中。求心不可得。何以故。心之自性。即诸法性。诸法性空。即真实性。由是义故。汝今不应妄生怖畏。是时化人闻佛说法。即白佛言。我今得悟罪业性空。不生怖畏。尔时实造业者。亦白佛言。我今得悟罪业性空。而不复生怖畏之心。

      【李文会曰】谓三世心无性可得。故可从缘而生。

      【肇法师云】闻说诸心。谓有实心。故须破遣。明三世皆空。故云过去已灭。未来未起。现在虚妄。三世推求。了不可得。故云若悟无法无相无事平常真心。即法体空寂。不生不灭。湛然清净。岂有前念今念后念可得也。

      【马祖云】道不用修。但莫洿染。何谓洿染。但有生死造作趣向。皆是洿染。若欲直会其道。平常心即是道。何谓平常心。无造作。无是非。无取舍。无憎爱。无圣凡。是故经云。非凡夫行。非圣贤行。是菩萨行。

      【赵州问南泉云】如何是道。泉云。平常心是道。

      【圜悟禅师颂曰】欲识平常道。天真任自然。行船宜举棹。走马即加鞭。若遇饥来饭。还应困即眠。尽从缘所得。所得亦非缘。

根据文件句柄,获取文件名

#include <windows.h>

#include <stdio.h>

#include <tchar.h>

#include <string.h>

#include <psapi.h>

 

#define BUFSIZE 512

 

BOOL GetFileNameFromHandle(HANDLE hFile) 

{

  BOOL bSuccess = FALSE;

  TCHAR pszFilename[MAX_PATH+1];

  HANDLE hFileMap;

 

  // Get the file size.

  DWORD dwFileSizeHi = 0;

  DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi); 

 

  if( dwFileSizeLo == 0 && dwFileSizeHi == 0 )

  {

     printf("Cannot map a file with a length of zero.\n");

     return FALSE;

  }

 

  // Create a file mapping object.

  hFileMap = CreateFileMapping(hFile, 

                    NULL, 

                    PAGE_READONLY,

                    0, 

                    1,

                    NULL);

 

  if (hFileMap) 

  {

    // Create a file mapping to get the file name.

    void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);

 

    if (pMem) 

    {

      if (GetMappedFileName (GetCurrentProcess(), 

                             pMem, 

                             pszFilename,

                             MAX_PATH)) 

      {

 

        // Translate path with device name to drive letters.

        TCHAR szTemp[BUFSIZE];

        szTemp[0] = '\0';

 

        if (GetLogicalDriveStrings(BUFSIZE-1, szTemp)) 

        {

          TCHAR szName[MAX_PATH];

          TCHAR szDrive[3] = TEXT(" :");

          BOOL bFound = FALSE;

          TCHAR* p = szTemp;

 

          do 

          {

            // Copy the drive letter to the template string

            *szDrive = *p;

 

            // Look up each device name

            if (QueryDosDevice(szDrive, szName, BUFSIZE))

            {

              UINT uNameLen = _tcslen(szName);

 

              if (uNameLen < MAX_PATH) 

              {

                bFound = _tcsnicmp(pszFilename, szName, 

                    uNameLen) == 0;

 

                if (bFound) 

                {

                  // Reconstruct pszFilename using szTemp

                  // Replace device path with DOS path

                  TCHAR szTempFile[MAX_PATH];

                  _stprintf(szTempFile,

                            TEXT("%s%s"),

                            szDrive,

                            pszFilename+uNameLen);

                  _tcsncpy(pszFilename, szTempFile, MAX_PATH);

                }

              }

            }

 

            // Go to the next NULL character.

            while (*p++);

          } while (!bFound && *p); // end of string

        }

      }

      bSuccess = TRUE;

      UnmapViewOfFile(pMem);

    } 

 

    CloseHandle(hFileMap);

  }

  printf("File name is %s\n", pszFilename);

  return(bSuccess);

}

开机自动运行

  其中strPath参数表示要设置为自运行的程序的绝对路径。当设置成功时返回true,否则返回false

BOOL SetAutoRun(CString strPath)//开机自动运行

{

   CString str;

   HKEY hRegKey;

   BOOL bResult;

   str=_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run");

   if(RegOpenKey(HKEY_LOCAL_MACHINE, str, &hRegKey) != ERROR_SUCCESS) 

       bResult=FALSE;

   else

   {

       _splitpath(strPath.GetBuffer(0),NULL,NULL,str.GetBufferSetLength(MAX_PATH+1),NULL);

       strPath.ReleaseBuffer();

       str.ReleaseBuffer();

       if(::RegSetValueEx( hRegKey,

                           str,

                           0,

                           REG_SZ,

                           (CONST BYTE *)strPath.GetBuffer(0),

                           strPath.GetLength() ) != ERROR_SUCCESS)

          bResult=FALSE;

       else

          bResult=TRUE;

       strPath.ReleaseBuffer();

   }

   return bResult;

}        

 

使计算机休眠

void XiuMian() 

{

 if(MessageBox("确实要休眠吗?","关机程序",MB_YESNO|MB_DEFBUTTON2|MB_ICONQUESTION)==IDYES)

 {

  HANDLE hToken;

  TOKEN_PRIVILEGES tp;

  LUID luid;

  if(::OpenProcessToken(GetCurrentProcess(),

         TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,

         &hToken))

  {

   ::LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&luid);

   tp.PrivilegeCount=1;

   tp.Privileges[0].Luid =luid;

   tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;

   ::AdjustTokenPrivileges(hToken,false,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL);

  }

  ::SetSystemPowerState(false,true); 

 }

}

本来不想写这篇很挨打的Post,不过在最近几天的最热的几篇Post里面看到无数的Tx为了面向对象的争论,感触颇多,遂作此篇。
鄙视OO的也进来鄙视我吧。望OO达人多多指正。

前头有一篇关于对象持久化的。不过很多Tx,连OO都不理解,那么持久化也是空谈。
我们首先抛弃千篇一律的什么对象来源于生活,是真是对象的程序表现的屁话,空洞,对于一个一接触程序就开始过程,将严谨的过程渗透到骨髓里的工程人员来说,跟他扯这个简直是对牛谈琴。所以我们从过程来讲对象,然后来看如何将过程化的思维方式转化到对象式的方法论。以及过程抽象的弊端以及为什么要在复杂系统里OO。
我们不能否认,我们在描述一件事情的时候,可以按照过程的方式进行抽象。打个比方。
=======================
将一个表单存入数据库
=======================
按照过程的方式描述。
1.一条条读取表单上字段的值
2.根据值构建SQL语句
3.创建链接
4.创建命令
5.执行命令
6.关闭链接
基本上也就6步就完成了,没有多余的步骤。同意?

那么接下来我们把这个命题扩展到两个表单乃至N个表单,那么这个过程就会N倍的扩张。于是过程达人们就开始动脑筋了。于是开始了第一个迭代的抽象(根据怪怪的理论,我们认同过程的抽象)。我们把构建SQL语句的部分过程抽象出来成了单独的过程-[构建Sql语句]。再一看,我们其实对执行SQL都可以抽象,于是将3,4,5,6都抽象出来,于是SqlHelper诞生了。

这就是过程抽象的方式,不可否认其实我们很多人都经历过这个阶段。而且对此很为困惑。如果所做的很简单,过程很短,那么看起来过程的抽象方式确实更加容易理解。但是我们来看看过程的噩梦在那里。

我们继续扩充命题。
现在我们提交一个表单需要提交到两个数据库。于是我们的N个表单又产生更多的子过程。随着单个过程的长度增加,我们抽象出来的子过程还可能抽象出子过程,结果抽象出来的子过程呈级数增加。直到你的脑袋爆掉。
还有一个问题就是,我们抽象出来的子过程放哪里?C#是一个面向对象的语言,所有的过程(或者函数,方法)都是从属于类,于是就出现了一个巨大的类包含无数静态方法的怪胎。

那么,我们来看面向对象。面向对象和过程有什么联系呢。其实程序最终的运行还是按照过程的,类的作用就像一个框框,指定了那个过程应该属于那个类,必须在存在对象的时候才能调用还是在类中调用。之前在过程的时候抽象出的一大堆子过程杂乱无章。但是在用面向对象的方法所抽象出来的对象确实井然有序的。
对象在执行的时候是按照一定的时序和过程在执行(所以在UML里有时序图)。但是我们不能够因为计算机是按照过程在执行就否定了OO,因为OO是设计时的概念(所以只有OOA、OOD、OOP而没有OOR【面向对象执行-自我发明的词,不存在此物】),而OO的过程是在设计的时候自然而然的产生的,既然OO的语言都存在,那么在这么多年的实践中,证明OO的方法是行之有效的。

下面我们来看如何OO。
用一个很简单的例子,聊天室,不用数据库的,数据记录在内存。
 

       虽然聊天室比较简单,但是我们还是通过用例分析来构建业务逻辑的领域模型。首先我们来找用例。下面是我们对聊天室业务的描述:

       建立一个聊天系统,用户在登陆后方可发言,用户可以看到所有人的公共发言和其他用户发给自己的私聊信息,用户可以发布公共信息和单独发送私聊信息给某个用户。用户可以自己创建聊天的房间,并作为聊天的主持人拥有对房间的控制权,可以踢人出房间。用户可以加入任何一个房间开始聊天。

       我们通过上面的描述可以得到两个粗略的实体:用户,房间。然后画个用例图来看看:


 

       然后我们分别建立这几个用例的用例卡片:

创建房间的用例

用例名称

创建房间

用例描述

用户创建一个自己控制的聊天区域

事件流

(用户已登陆)用户输入要创建的房间名称后系统创建一个新房间,创建房间的用户自动进入房间

备选流1

(用户已登陆,已经创建了一个房间)提示用户一次只能创建一个房间

备选流2

(用户未登录)按钮无发点击

进入房间的用例

用例名称

进入房间

用例描述

用户选择一个聊天区域进入

事件流

用户进入一个聊天区域后获取当前公共聊天区域的信息,

发布公共信息的用例

用例名称

发布公共信息

用例描述

用户向其他所有用户发布信息,所有的用户都能够收到

事件流

(用户已登陆)用户输入要发布的信息后将信息发送给所有用户

备选流1

(用户未登录)不能发布

发布私聊信息的用例

用例名称

发布私聊信息

用例描述

用户给指定用户发送消息

事件流

(用户已登陆)用户选择要发送消息的用户和要发送的信息后将信息发送给指定用户

备选流1

(用户已登陆,要发送的用户不存在)提示用户要发送的用户不存在

备选流2

(用户未登录)不能发布

踢人的用例

用例名称

踢人

用例描述

创建房间的用户将选定用户踢出房间

事件流

(用户已登陆,是房间创建者)用户选择踢的用户,改变该用户的状态为被踢

备选流1

(用户已登陆,但不是房间创建者)看不到此功能

备选流2

(用户未登录)看不到此功能

       我们根据上面的用例描述基本覆盖了大部分业务的流程。我们在其中抽象出下面的对象:

l  聊天系统

l  房间

l  用户

l  消息

l  消息区域

 

现在我们来分析这几个对象之间的关系,首先是聊天系统,因为是多人的系统,所以比如说创建房间的行为,虽然是用户发出的,但是实际上的创建者和拥有者却是系统,我们需要一个容器来容纳所有的对象。所以其实换一个思路来说,系统对象就相当于一个大楼,而房间就应该在大楼里。因为系统里有多个房间,所以系统里应该有一个房间对象的列表。

       因为房间是有名字的,所以为了方便检索,我们需要将房间的名字作为索引。因为我们要从系统找到房间,就需要提供一个查找房间的行为,而且系统也要提供房间的列表。最后我们得到了一个类图:


       然后我们来看房间,房间包含在系统中,被用户通知系统来创建,在创建的时候要给房间一个名字,以方便用户来找到这个房间。房间里有什么呢?房间里有人,也就是在交谈中的人,所以我们要在房间里准备椅子,也就是聊天用户对象的列表。因为是不见面的聊天,所以每个人都将要说的话写下来(一个消息),然后如果是给所有人看的,就贴到公共的留言板上(消息区域),如果是发给某个人的就把消息交给某个用户,所以房间的程序(房间对象)需要两个处理消息的行为,一个处理公共消息,一个处理私人消息。最后将用户清除出房间的行为也需要房间来执行(因为用户包含在房间里,所以根据封装的原则,这个行为只能由房间来执行,当然可能不一定是房间发起的)。最后我们得到了类图:

 

接下来我们继续分析用户对象。因为很多动作都是用户主动发出的。首先用户对象必须实现所有用例上的行为:

l  创建房间

l  进入房间

l  发公共信息

l  发私聊信息

l  踢人

然后再来分析用户的属性,首先用户在一个房间内,那么用户需要有一个房间的属性,在进入一个房间就具备这一个房间的属性。然后我们还需要一个属性用来表示用户的状态。还有就是用户收到的消息需要一个地方来存,所以需要一个用户消息的存储区域,所以需要一个消息池。最后得到类图:




 

       消息池对象是属于用户和房间的,算是一个公共的结构,作用就是用来存放消息,并且从特性上来说是一个栈结构的容器,也就是FIFO队列。而由于这个类的对象的生命周期都是由房间或者用户来维护管理,所以基本上属于内部对象,这里也就不用画关系了。消息池需要几个行为:添加消息、清除消息两个方法。这两个方法主要出于封装上的需要,主要是用来提高整个对象的内聚减小耦合。类图如下:



 

最后是消息对象,消息对象基本上算是DTO对象,用来传递信息的,所以只需要值属性就可以了。

最后我们初步的通过分析整个业务逻辑的问题域得到了体现领域模型的类结构。我们在之后会继续修改完善这个模型,因为如果我们在某个过程的实现当中需要操作另外一个对象的内部成员的时候,那么我们就需要在目标对象上增加属性,或者是增加方法,这里对于值对象我们推荐用属性,而对于一些需要操作的复杂对象,比如列表,我建议使用方法。

最后我们实现这几个类就OK拉。



原文:http://www.cnblogs.com/Alexander-Lee/archive/2007/09/19/898610.html

闲话不说了,看代码:

 1#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L)
 2
 3BOOL Lock_CurrentProcess()
 4{
 5  HANDLE hProcess = ::GetCurrentProcess();
 6  SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY;
 7  PSID pSid;
 8  BOOL bSus = FALSE;
 9  bSus = ::AllocateAndInitializeSid(&sia,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&pSid);
10  if(!bSus) goto Cleanup;
11  HANDLE hToken;
12  bSus = ::OpenProcessToken(hProcess,TOKEN_QUERY,&hToken);
13  if(!bSus) goto Cleanup;
14  DWORD dwReturnLength;
15  ::GetTokenInformation(hToken,TokenUser,NULL,NULL,&dwReturnLength);
16  if(dwReturnLength > 0x400goto Cleanup;
17  LPVOID TokenInformation;
18  TokenInformation = ::LocalAlloc(LPTR,0x400);//这里就引用SDK的函数不引用CRT的了
19  DWORD dw;
20  bSus = ::GetTokenInformation(hToken,TokenUser,TokenInformation,0x400,&dw);
21  if(!bSus) goto Cleanup;
22  PTOKEN_USER pTokenUser = (PTOKEN_USER)TokenInformation;
23  BYTE Buf[0x200];
24  PACL pAcl = (PACL)&Buf;
25  bSus = ::InitializeAcl(pAcl,1024,ACL_REVISION);
26  if(!bSus) goto Cleanup;
27  bSus = ::AddAccessDeniedAce(pAcl,ACL_REVISION,0x000000FA,pSid);
28  if(!bSus) goto Cleanup;
29  bSus = ::AddAccessAllowedAce(pAcl,ACL_REVISION,0x00100701,pTokenUser->User.Sid);
30  if(!bSus) goto Cleanup;
31  if(::SetSecurityInfo(hProcess,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,NULL,NULL,pAcl,NULL) == 0)
32    bSus = TRUE;
33Cleanup:
34  if(hProcess != NULL)
35    ::CloseHandle(hProcess);
36  if(pSid != NULL)
37    ::FreeSid(pSid);
38  return bSus;
39}

40


这段代码就可以锁住其他进程打开本进程,当然也就防止了注入,和读写内存.

可以更绝点Denied ALL ACCESS(0xFFFFFFFF)就连结束都不可能了

::AllocateAndInitializeSid 可以换成 :: InitializeSid .因为我们并不需要初始化子Sid.
另外.
  bSus = ::AddAccessDeniedAce(pAcl,ACL_REVISION,0x000000FA,pSid);
  if(!bSus) goto Cleanup;
  bSus = ::AddAccessAllowedAce(pAcl,ACL_REVISION,0x00100701,pTokenUser->User.Sid);
实际上只需要下面的一句,或者干脆把它去掉,因为如果不添加Ace默认就是没有权限.既然这样上面的那句话AllocateAndInitializeSid 也可以省掉,也似乎有些多余

Lua logo Lua 5.0 参考手册

作者: Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright © 2003 Tecgraf, PUC-Rio. All rights reserved.

译者:ShiningRay Nicholas @ NirvanaStudio

给予支持

1 - 绪论

Lua是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。它同样可以对面向对象语言、函数式程序设计(Functional Programming,如Lisp)以及数据驱动编程(data-driven programming)提供很好的支持。它的目标是被用作一种强大的、轻型的配置语言。Lua目前已经被实现为一个扩展库,是用clean C (ANSI C/C++的一个通用子集)编写的。

作为一个扩展语言,Lua没有"Main"函数的概念:它仅仅是嵌入一个宿主程序进行工作,可以称之为 嵌入式编程 或者简单的说是 宿主编程。这个宿主程序可以调用函数来执行Lua的代码片断,可以设置和读取Lua的变量,可以注册C函数让Lua代码调用。Lua的能力可以扩展到更大范围,在不同的领域内,这样就在同样的语法框架下创建了你自定义的编程语言。

Lua的发行版包括一个独立的嵌入式程序,lua,他使用Lua的扩展库来提供一个完全的Lua解释器。

Lua是自由软件,通常不提供任何担保,如它的版权说明中叙述的那样。 手册中描述的实现在Lua的官方网站可以找到,www.lua.org

如果需要知道Lua设计背后的一些决定和讨论,可以参考以下论文,它们都可以在Lua的网站上找到。

  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language. Software: Practice & Experience26 #6 (1996) 635-652.
  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The design and implementation of a language for extending applications. Proceedings of XXI Brazilian Seminar on Software and Hardware (1994) 273-283.
  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb's Journal21 #12 (Dec 1996) 26-33.
  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages (2001) B-14-B-28.

Lua在葡萄牙语中的意思是“月亮”,发音是 LOO-ah。

2 - 语言

这一章将描述Lua的词法、语法和语义结构。换句话说,这一章会讲什么标记是合法的,他们是如何组合的,以及他们的组合是什么含义。

语言结构会使用常用的扩展BNF范式来解释,如{a} 表示0或多个a, [a] 表示a是可选的(0个或1个)。非终端字体(不能显示的)用 斜体表示,关键字是粗体,其他终端符号用typewriter(等宽)字体,并用单引号引出。

2.1 - 词法约定

Lua中的标识符(Identifiers)可以是任意的数字、字符和下划线“_”,但不能以数字开头。这条规则符合大多数编程语言中的标识符的定义。(字符的具体定义要根据系统的地区设置:任何区域设置可以认同的字母表中的字母都可以用在标识符中。)

下面的关键字(keywords)为保留关键字不可以作为标识符出现:

       and       break     do        else      elseif

   end       false     for       function  if

   in        local     nil       not       or

   repeat    return    then      true      until     while

Lua对大小写敏感:and是一个保留字,但是 AndAND 是两个不一样的、但都合法的标识符。习惯上来说,以下划线开始且后面跟着大写字母的标识符 (例如 _VERSION) 是为Lua内部变量所保留的。

下面的字符(串)是其他的一些标记:

       +     -     *     /     ^     =

   ~=    &lt;=    &gt;=    &lt;     &gt;     ==

   (     )     {     }     [     ]

   ;     :     ,     .     ..    ...

字符串(Literal strings) 以单引号或者双引号定界,同时可以包含以下C语言风格的转义字符:

  • \a --- 铃声(bell)
  • \b --- 回退(backspace)
  • \f --- form feed
  • \n --- 新行(newline)
  • \r --- 回车(carriage return)
  • \t --- 水平制表符(horizontal tab)
  • \v --- 垂直制表符(vertical tab)
  • \\ --- 反斜杠(backslash)
  • \" --- 双引号(quotation mark)
  • \' --- 单引号(apostrophe)
  • \[ --- 左方括号(left square bracket)
  • \] --- 右方括号(right square bracket)

另外,一个 `\newline´ (一个反斜杠加上一个真正的换行符)会导致字符串内的分行。字符串中的字符也可以使用转义字符`\ddd´通过数字值来指定。ddd 是最多为3个十进制数字的序列。Lua中的字符串也可以包含8进制数字,包括嵌入零,它可以表示为 `\0´。

字符串也可以用双方括号来定界[[ · · · ]]。这种括号方式的语法,字符串可以跨越多行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的 `[[´ 后面紧跟着一个换行符的话,这个换行符不会包括在字符串内。举个例子:在一个使用ASCII编码(其中`a´ 的编码是 97,换行符是 10,字符`1´ 是 49)的系统中,以下四种格式得到的都是同一个字符串:

      (1)   "alo\n123\""

  (2)   '\97lo\10\04923"'

  (3)   [[alo

        123"]]

  (4)   [[

        alo

        123"]]

数值常量(Numerical constants) 可以有一个可选的底数部分和一个可选的指数部分。以下是有效的数值常量:

       3     3.0     3.1416  314.16e-2   0.31416E1

注释(Comments) 可以在任何地方出现,必须在最前面加上双减号 (--)。如果紧接着 -- 的文本不是 [[,那么会认为是一个 短注释(short comment), 这一行往后到行尾都是注释。否则,会认为是一个 常注释(long comment),注释直到相应的 ]]结束。长注释可以跨越多行,同时可以包含嵌套的 [[ · · · ]] 括号对。

为了方便起见,文件的第一行如果是以#开始,这个机制允许Lua在Unix系统中用做一个脚本解释器(见 6)。

2.2 - 值和类型

Lua是一种 动态类型语言(dynamically typed language)。这意味着变量是没有类型的;只有值才有。语言中没有类型定义。所有的值都包含他自身的类型。

Lua中有八种基本类型:nil, boolean, number, string, function, userdata, threadtableNil 空类型只对应 nil值,他的属性和其他任何值都有区别;通常它代表没有有效的值。 Boolean 布尔类型有两种不同的值 false and true。在Lua中, nil and false 代表成假条件;其他任何值都代表成真条件。 Number 数字类型表示实数(双精度浮点数)。(构建Lua解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。 String 字符串类型表示一个字符的序列。Lua 字符串可以包含8位字符,包括嵌入的 ('\0') (见 2.1)。

函数是Lua中的 第一类值(first-class values)。也就是说函数可以保存在变量中,当作参数传递给其他函数,或者被当作结果返回。Lua可以调用(和处理)Lua写的函数和C写的函数 (见 2.5.7)。

用户数据类型(userdata) 提供了让任意C数据储存在Lua变量中的功能。这种类型直接对应着一块内存,Lua中也没有任何预先定义的操作,除了赋值和一致性比较。然而,通过使用 元表(metatables),程序员可以定义处理userdata的操作。(见 2.8)。 Userdata 值不能在Lua中建立或者修改,只能通过 C API。这保证了宿主程序的数据完整性。

线程(thread) 类型代表了相互独立的执行线程,用来实现同步程序。

表(table) 类型实现了联合数组,也就是说,数组不仅可以使用数字,还能使用其他的值(除了 nil)。 而且,tables 可以是 互异的(heterogeneous),他们可以保存任何类型的值(除了 nil)。 Tables 是Lua中唯一的数据结构机制;他们可以用来表示一般数组,特征表,集合,记录,图,树等等。如果要表示记录,Lua使用字段名作为索引。语言支持 a.name 这种比较优美的表示方式,还有 a["name"]。在Lua中有几种建立表的简便方法 (见 2.5.6)。

就像索引一样,表字段的值也可以是任何类型(除了 nil)。特别需要注意地是,由于函数是第一型的值,表字段也可以包含函数。这样表也可以支持 方法(methods) (见 2.5.8)。

表,函数,和用户数据类型的值都是 对象(objects):变量不会包含他们的实际值,只是一个他们的引用(references)。 赋值,参数传递和函数返回只是操作这些值的引用,这些操作不会暗含任何拷贝。

库函数 type 返回一个字符串描述给出值所表示的类型 (见 5.1)。

2.2.1 - 类型转换

Lua提供运行时的数字和字符串值得自动转换。任何对字符串的算术操作都会现尝试把字符串转换成数字,使用一般规则转换。反过来,当一个数值用在需要字符串的地方时,数字会自动转换成字符串,遵循一种合理的格式。如果要指定数值如何转换成字符串,请使用字符串库中的 format 函数(见 5.3)。

2.3 - 变量

变量是储存值的地方。Lua中有三种不同的变量:全局变量,局部变量和表字段。

一个名称可以表示全局变量或局部变量(或者一个函数的正式参数,一种局部变量的特殊形式):

	var ::= Name

Lua假设变量是全局变量,除非明确地用local进行声明 (见 2.4.7)。局部变量有 词义范围(lexically scoped):局部变量可以被在它们范围内的函数自由访问 (见 2.6)。

在变量第一次赋值之前,它的值是 nil

方括号用于对表进行检索:

	var ::= prefixexp `[´ exp `]´



第一个表达式 (prefixexp)结果必须是表;第二个表达式 (exp) 识别表中一个特定条目。给出表的表达式有一个限制语法;详细见 2.5。

var.NAME 语法是 var["NAME"] 的较好形式:

	var ::= prefixexp `.´ Name



访问全局变量和表字段的实质可以通过元表进行改变。对索引变量 t[i] 的访问等同于调用 gettable_event(t,i)。(关于 gettable_event 的完整描述见 2.8。这个函数并没有在Lua中定义,也无法调用。我们在这里仅仅用来解释原理)。

所有的全局变量存在一个普?ǖ腖ua表中,称之为 环境变量表(environment tables) 或简称 环境(environments)。由C写的并导入到Lua中的函数 (C 函数) 全部共享一个通用 全局环境(global environment)。Lua写的每个函数 (a Lua 函数) 都有一个它自己的环境的引用,这样这个函数中的所有的全局变量都会指向这个环境变量表。当新创建一个函数时,它会继承创建它的函数的环境。要改变或者获得Lua函数的环境表,可以调用 setfenv or getfenv (见 5.1)。

访问全局变量 x 等同于 _env.x,又等同于

       gettable_event(_env, "x")

_env 是运行的函数的环境。(_env 变量并没有在Lua中定义。我们这里仅仅用来解释原理)

2.4 - 语句

Lua支持一种很通俗的语句集,和Pascal或者C中的很相似。他包括赋值,控制结构,过程调用,表构造和变量声明。

2.4.1 - 语句段

Lua执行的最小单元称之为一个 段(chunk)。一段语句就是简单的语句的序列,以顺序执行。每一个语句后面都可以加上一个分号(可选):

	chunk ::= {stat [`;´]}

Lua将语句段作为一个匿名函数 (见 2.5.8) 的本体进行处理。这样,语句段可以定义局部变量或者返回值。

一段语句可以储存在文件内或者宿主程序的一个字符串中。当语句段被执行时,他首先被预编译成虚拟机使用的字节码,然后虚拟机用一个解释器执行被编译的代码。

语句段也可以被预编译为二进制代码;详情参看 luac 程序。源代码和编译形态可以互相转换;Lua自动监测文件类型然后作相应操作。

2.4.2 - 语句块

一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:

	block ::= chunk

一个语句块可以明确定界来替换单个语句:

	stat ::= do block end

显式语句块可以很好地控制变量的声明范围。显示语句块有时也常会在另一个语句块的中间添加 returnbreak 语句 (见 2.4.4)。

2.4.3 - 赋值

Lua允许多重赋值。因此,赋值的语法定义为:等号左边是一个变量表,右边是一个表达式表。两边的表中的元素都用逗号分隔开来:

	stat ::= varlist1 `=´ explist1

varlist1 ::= var {`<B>,</B>´ var}

explist1 ::= exp {`<B>,</B>´ exp}

我们将在 2.5 讨论表达式。

在赋值之前,值的表长度会被 调整 为和变量的表一样。如果值比需要的多,多出的值就会被扔掉。如果值的数量不够,就会用足够多的 nil 来填充表直到满足数量要求。如果表达式表以一个函数调用结束,那么在赋值之前,函数返回的所有的值都会添加到值的表中(除非把函数调用放在括号里面;见 2.5)。

赋值语句首先计算出所有的表达式,然后才会执行赋值,所以代码:

       i = 3

   i, a[i] = i+1, 20

设置 a[3] 为 20,但不影响 a[4]。因为在 a[i] 中的 i 在赋值为4之前是等于3。同样的,下面这行:

       x, y = y, x



可以交换 xy 的值。

对全局变量和表字段的赋值可以看作是通过元表进行的。对一个索引变量的赋值 t[i] = val 等同于 settable_event(t,i,val)。 (settable_event详细介绍参看 2.8 ,Lua中并未定义该函数,他也无法直接调用。我们这里只是用它来进行解释。)

对全局变量的赋值 x = val 等同于赋值语句 _env.x = val,像前面也等同于:

       settable_event(_env, "x", val)

_env 是运行函数的环境。(_env 变量并未在Lua中定义。我们这里只是用来进行解释。)

2.4.4 - 控制结构

控制结构 if, whilerepeat 具有通用的含义和类似的语法:

	stat ::= while exp do block end



stat ::= <B>repeat</B> block <B>until</B> exp

stat ::= <B>if</B> exp <B>then</B> block {<B>elseif</B> exp <B>then</B> block} [<B>else</B> block] <B>end</B></PRE>

Lua也有 for 语句,有两种格式 (见 2.4.5)。

控制结构的条件表达式 exp 可以返回任意值。falsenil 都表示假。所有其他的值都认为是真(特别要说明的:数字0和空字符串也表示真)。

语句 return 用来从函数或者是语句段中返回一个值。函数和语句段都可以返回多个值,所以 return 语句的语法为:

	stat ::= return [explist1]

break 语句可以用来终止while, repeat 或者 for 循环的执行,直接跳到循环后面的语句。

	stat ::= break

break 结束最里面的一个循环。

由于语法的原因, returnbreak 语句只能作为语句块的 最后一个 语句。如果确实需要在语句块的中间使用 return 或者 break,需要使用一个显示语句块: `do return end´ 和 `do break end´,这样现在 returnbreak 就成为他们(内部)语句块中的最后一个语句了。实际上,这两种用法一般只用在调试中。

2.4.5 - For 语句

for 语句有两种形式:数值形式和一般形式。

数值形式的 for 循环根据一个控制变量用算术过程重复一语句块。语法如下:

	stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end

block 语句块根据 name 以第一个 exp 的值开始,直到他以第三个 exp 为步长达到了第二个 exp。一个这样的 for 语句:

       for var = e1, e2, e3 do block end

等价于一下代码:

       do

     local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)

     if not (var and _limit and _step) then error() end

     while (_step&gt;0 and var&lt;=_limit) or (_step&lt;=0 and var&gt;=_limit) do

       block

       var = var + _step

     end

   end

注意:

  • 三种控制表达式只会被计算一次,在循环开始之前。他们的结果必须是数值。
  • _limit_step 是不可见的变量。这里只是为了进行解释。
  • 如果你在程序块内给 var 赋值,结果行为将会不确定。
  • 如果没有给出第三个表达式(步长),那么默认为1。
  • 你可以使用 break 来退出 for 循环。
  • 循环变量 var 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。

for 的语句的一般形式是操作于函数之上的,称之为迭代器(iterators)。每一个迭代过程,它调用迭代函数来产生新的值,直到新的值是 nil 。一般形式 for 循环有如下语法:

	stat ::= for Name {`,´ Name} in explist1 do block end

一个这样的 for 语句

       for var_1, ..., var_n in explist do block end

等同于以下代码:

       do

     local _f, _s, var_1 = explist

     local var_2, ... , var_n

     while true do

       var_1, ..., var_n = _f(_s, var_1)

       if var_1 == nil then break end

       block

     end

   end

注意:

  • explist 只会计算一次。他的结果是一个 迭代 函数,一个 状态,和给第一个 迭代变量的一个初始值。
  • _f_s 是不可见的变量。这里只是用来进行解释说明。
  • 如果你在语句块中给 var_1 赋值,那么行为就会变得不确定。
  • 你可以使用 break 来退出 for 循环。
  • 循环变量 var_i 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。

2.4.6 - 语句式函数调用

如果要忽略可能的影响,函数调用可以按照语句执行:

	stat ::= functioncall

I在这里,所有的返回值都会被忽略。函数调用将在 2.5.7 详细解释。

2.4.7 - 局部变量声明

局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:

	stat ::= local namelist [`=´ explist1]

namelist ::= Name {`<B>,</B>´ Name}

如果出现初始赋值,他的语法和多重赋值语句一样(见 2.4.3)。否则,所有的变量都会初始化为 nil

一个语句段也是一个语句块(见 2.4.1),所以语句段之内的任何显式语句块之外也可以声明局部变量。这种局部变量在语句段结束就会销毁。

局部变量的可见规则会在 2.6解释。

2.5 - 表达式

Lua中有以下几种基本表达式:

	exp ::= prefixexp

exp ::= <B>nil</B> | <B>false</B> | <B>true</B>



exp ::= Number

exp ::= Literal

exp ::= function

exp ::= tableconstructor

prefixexp ::= var | functioncall | `<B>(</B>´ exp `<B>)</B>´

数字和字符串已经在 2.1 中解释;变量在 2.3 中解释;函数定义在 2.5.8;函数调用在 2.5.7;表构造器在 2.5.6。

一个用括号括起的表达式只会返回一个值。这样,(f(x,y,z)) 将只会返回单一的一个值,即使 f 可以返回多个值,((f(x,y,z)) 的值将是 f 返回的第一个值或者如果 f 没有返回任何值就是 nil )。

表达式也可以使用各种算术运算符,关系运算符和逻辑运算符,下面几节就会讲到。

2.5.1 - 算术运算符

Lua支持常见的几种运算符:二元 + (加), - (减), * (乘), / (除), 以及 ^ (指数运算); 一元 - (负号)。如果操作数是数字,或者是可以转换成数字的字符串(见 2.2.1),那么所有的操作都和算术意义上的运算一致(除了指数)。指数运算其实是调用一个全局函数 __pow,否则一个合适的元方法将会被调用(见 2.8)。标准数学库定义了函数 __pow,给出了指数运算的定义(见 5.5)。

2.5.2 - 关系运算符

Lua中的关系运算符有

       ==    ~=    <     >     <=    >=

这些运算只会产生 falsetrue值。

等于 (==) 先比较操作数的类型。如果类型不一样,结果便是 false。否则,再比较操作数的值。对象(表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象的时候,才认为是相等。每次你创建一个新的对象(表,用户数据,或者是函数)。这个新的对象将不同于前面存在的任何对象。

你可以用"eq"元方法改变Lua比较表的方式(见 2.8)。

2.2.1 的转换规则 不适用 于相等比较。这样," "0"==0 结果是 false ,同样 t[0]t["0"] 给出的是表中不同的字段。

而操作符 ~= 是等于 (==) 的相反的操作。

T操作符的执行顺序如下。如果两个参数都是数字,那么它们就直接进行比较。如果,两个参数都是字符串,那么它们的值会根据当前的区域设置进行比较。否则,Lua尝试调用"lt"或者 "le" 元方法(见 2.8)。

2.5.3 - 逻辑运算符

Lua中的逻辑运算符是:

       and   or    not



和控制结构一样(见 2.4.4),所有的逻辑操作符认为 falsenil 都?羌伲渌闹刀际钦妗?

not 操作符总是返回 falsetrue

合取运算 and 如果第一个参数是 false 或者 nil 则返回第一个参数;否则 and 返回第二个参数。析取运算 or 如果第一个参数不是 nilfalse 则返回第一个参数,否则 or 返回第二个参数。 andor 都使用截取计算,也就是,只有有必要的情况下才计算第二个参数。例如:

       10 or error()       -> 10

   nil or "a"          -&gt; "a"

   nil and 10          -&gt; nil

   false and error()   -&gt; false

   false and nil       -&gt; false

   false or nil        -&gt; nil

   10 and 20           -&gt; 20

2.5.4 - 串联接

在Lua中字符串连接操作符是两个点 (`..´)。如果两边的操作数都是字符或者数字,他们就都会按照 2.2.1的规则被转换成字符串。否则,将调用 "concat" 元方法(见 2.8)。

2.5.5 - 优先级

Lua中的操作符的优先级如下表所示,从低到高优先级:

       or

   and

   &lt;     &gt;     &lt;=    &gt;=    ~=    ==

   ..

   +     -

   *     /

   not   - (unary)

   ^

表达式中,你可以使用括号来改变优先顺序。串联接符 (`..´) 和指数符 (`^´) 都是右结合的。其他二元操作都是左结合的。

2.5.6 - 表构造器

表构造器是创建表的表达式。当计算构造器的时候,就会创建一个新的表。构造器可以用来创建空的表,或者创建表并初始化一些字段。一般的语法如下:

	tableconstructor ::= `{´ [fieldlist] `}´

fieldlist ::= field {fieldsep field} [fieldsep]

field ::= `<B>[</B>´ exp `<B>]</B>´ `<B>=</B>´ exp | Name `<B>=</B>´ exp | exp

fieldsep ::= `<B>,</B>´ | `<B>;</B>´

[exp1] = exp2 形式的每一个添加到新表中的字段条目以 exp1 为键并以 exp2 为值。name = exp 形式的字段,等同于 ["name"] = exp。最后,exp 形式的字段等同于 [i] = exp 其中 i 是连续的整数,从1开始。其它格式的字段不会影响它的计数。例如:

       a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45}

等同于:

       do

     local temp = {}

     temp[f(1)] = g

     temp[1] = "x"         -- 1st exp

     temp[2] = "y"         -- 2nd exp

     temp.x = 1            -- temp["x"] = 1

     temp[3] = f(x)        -- 3rd exp

     temp[30] = 23

     temp[4] = 45          -- 4th exp

     a = temp

   end

如果列表中最后一个字段的形式是 exp 同时表达式又是一个函数调用,那么调用返回的所有值会依次进入列表(见 2.5.7)。如果要避免这种情况,在函数调用两边加上括号(见 2.5)。

字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。

2.5.7 - 函数调用

Lua中的一个函数调用有如下语法:

	functioncall ::= prefixexp args

在函数调用中,首先会计算 prefixexpargs 。如果 prefixexp 的值是 function 类型,那么那个函数就会被调用,同时使用给出的参数。否则,他的 "call" 元方法就会被调用,第一个参数是 prefixexp 的值,接下来是原来的调用参数(见 2.8)。

形式

	functioncall ::= prefixexp `:´ Name args

可以用来调用“方法”("methods")。调用 v:name(...) 语法上比 v.name(v,...),要好一些,除非表达式 v 只计算一次。

参数可以有以下几种语法:

	args ::= `(´ [explist1] `)´

args ::= tableconstructor

args ::= Literal

所有的参数表达式都会在实际调用之前进行计算。f{...} 的调用形式在语法上较 f({...}) 要好,是因为,参数列表示一个单独的新表。 f'...' (或者 f"..." 或者 f[[...]]) 较 f('...') 要好,是因为参数列表是一个单独的字符串。

因为函数可以返回任意个结果(见 2.4.4),结果的数量必须在使用它们前进行调整。如果函数按照语句进行调用(见 2.4.6),那么它的返回列表就会被调整为零个元素,这样就舍弃了所有的返回值。如果调用函数时,他是一个表达式列表的最后一个元素,那么不会做调整(除非调用时加了括号)。

以下是一些例子:

       f()                -- 调整为0个结果

   g(f(), x)          -- f() 被调整成1个结果

   g(x, f())          -- g 获得 x 加上f()返回的所有值

   a,b,c = f(), x     -- f() 被调整成1个结果(此时c获得nil值)

   a,b,c = x, f()     -- f() 被调整为两个结果

   a,b,c = f()        -- f() 被调整为3个结果

   return f()         -- 返回所有 f() 返回的值

   return x,y,f()     -- 建立一个表包含所有 f() 返回的值

   {f()}              -- creates a list with all values returned by f()

   {f(), nil}         -- f() 被调整为一个结果

如果你用括号括起调用的函数,那么它就会被调整为返回一个值。

       return x,y,(f())   -- returns x, y, and the first value from f()

   {(f())}            -- creates a table with exactly one element

作为Lua语法自由格式的一个例外,你不能在函数调用的 `(´ 前面加入一个换行。这个限制可以避免语言中的一些二义性。如果你写:

       a = f

   (g).x(a)

Lua会读作 a = f(g).x(a)。这样,如果你想执行为两条语句,你必须在中间加分号。如果你实际上想调用 f,你就必须删除 (g) 前面的换行。

return functioncall 的调用格式称之为 尾部调用(tail call)。Lua实现了proper tail calls;在一个尾部调用中,被调用的函数将会重新使用调用程序的栈。因此,程序执行对嵌套尾部调用的次数没有任何限制。然而,尾部调用会清楚调用函数的调试信息。注意尾部调用只有在特殊的语法中才能出现,也就是 return 只有一个函数调用作为参数,这种语法保证了调用函数确切返回被调用函数的返回值。所以,下面的例子都不是尾部调用:

  return (f(x))        -- results adjusted to 1

return 2 * f(x)

return x, f(x) – additional results

f(x); return – results discarded

return x or f(x) – results adjusted to 1

2.5.8 - 函数定义

函数定义的语法是:

	function ::= function funcbody

funcbody ::= `<B>(</B>´ [parlist1] `<B>)</B>´ block <B>end</B></PRE>

下面较好的语法简化了函数定义:

	stat ::= function funcname funcbody

stat ::= <B>local</B><B>function</B> Name funcbody

funcname ::= Name {`<B>.</B>´ Name} [`<B>:</B>´ Name]

语句

       function f () ... end

会被翻译为

       f = function () ... end

语句

       function t.a.b.c.f () ... end

会被翻译为

       t.a.b.c.f = function () ... end

语句

       local function f () ... end

会被翻译为

       local f; f = function () ... end

一个函数定义是一个可执行的表达式,他的类型为 函数(function) 。当Lua预编译语句段的时候,他的函数体也会被预编译。这样,当Lua执行函数定义的时候,函数被 实例化封装 closed)。这个函数实例(或闭包 closure)是表达式的最终结果。同一个函数的不同的实例可以引用不同的外部局部变量也可以有不同的环境表。

形式参数(代表参数的变量,简称形参)就像用实际参数值(简称实参)初始化的局部变量一样。

	parlist1 ::= namelist [`,´ `...´]

parlist1 ::= `<B>...</B>´

当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是 variadic 或者 变长参数函数(vararg function)。变长参数函数在其参数列表最后有三个点 (`...´)。 变长参数函数不会对参数列表进行调整;而是,它把所有的额外实参放到一个隐含的形参 arg中。 arg 的值是一个表,包含一个字段 `n´ 表示额外参数的个数,位置 1, 2, ..., n是额外的参数。

请思考以下函数定义的例子:

       function f(a, b) end

   function g(a, b, ...) end

   function r() return 1,2,3 end

然后,我们有以下实参到形参的对应关系:

       CALL            PARAMETERS



   f(3)             a=3, b=nil

   f(3, 4)          a=3, b=4

   f(3, 4, 5)       a=3, b=4

   f(r(), 10)       a=1, b=10

   f(r())           a=1, b=2



   g(3)             a=3, b=nil, arg={n=0}

   g(3, 4)          a=3, b=4,   arg={n=0}

   g(3, 4, 5, 8)    a=3, b=4,   arg={5, 8; n=2}

   g(5, r())        a=5, b=1,   arg={2, 3; n=2}

结果使用 return 语句返回(见 2.4.4)。如果控制到达了函数尾部而没有遇到 return 语句,那么函数没有返回值。

冒号(:) 语法是用来定义 methods 的,也就是,函数有一个隐含的额外参数 self. 。这样,语句:

       function t.a.b.c:f (...) ... end

相对以下是较好的形式:

       t.a.b.c.f = function (self, ...) ... end



2.6 - 可见性规则

Lua是一个有词法范围的语言。变量的范围从声明语句后的第一个语句开始到包含声明的最内部的语句块为止。例如:

  x = 10                -- global variable

do – new block

local x = x         -- new `x', with value 10

print(x)            --&gt; 10

x = x+1

do                  -- another block

  local x = x+1     -- another `x'

  print(x)          --&gt; 12

end

print(x)            --&gt; 11

end

print(x) –> 10 (the global one)

注意:在类似 local x = x,正在声明的新的 x 尚未进入范围,所以第二个 x 指代的是外面的变量。

由于词法范围的规则,在局部变量的范围内定义的函数可以任意访问这些变量。例如:

  local counter = 0

function inc (x)

counter = counter + x

return counter

end

内部函数使用的局部变量在函数内部称之为 上值(upvalue),或者 外局部变量(external local variable)

注意每个 local 语句执行时会定义一个新的局部变量。看以下例子:

  a = {}

local x = 20

for i=1,10 do

local y = 0

a[i] = function () y=y+1; return x+y end

end

循环产生了十个闭包(也就是,十个匿名函数的实例)。每个闭包使用不同的 y 变量,但他们共享同一个 x 变量。

2.7 - 错误处理

因为Lua是一个扩展语言,所有的Lua动作都是从宿主程序中调用Lua库中函数的C代码开始的(见 3.15)。无论错误发生在Lua编译过程时或执行时,控制返回C,然后可以做相应的处理(比如打印一个错误)。

Lua代码可以通过调用error函数来产生一个错误(见 5.1)。如果你要在Lua中捕获错误,你可以使用 pcall 函数(见 5.1)。

2.8 - 元表 (Metatables)

Lua中的每一个表和用户数据都可以拥有一个 元表(metatable)。这个 元表 是一个普通的Lua表,定义了在特定操作下原始表和用户数据的行为。你可以通过设置一个对象的元表中的特定字段来更改它某些方面的行为。例如,当一个对象是一个加法的操作数时,Lua检查它的元表中的 "__add" 字段是不是一个函数。如果是,Lua调用它来执行加法。

我们称元表中的键(字段名,key)为 事件(events) ,值为 元方法(metamethods)。在上一个例子中, "add" 是事件,执行加法的函数是元方法。

你可以通过 set/getmetatable 函数来查询和更改一个对象的元表(见 5.1)。

元表可以控制对象在算术操作、比较、串连接、索引取值中如何运行。元表也可以定义一个函数当收集内存垃圾时调用。每一个操作这里Lua都用一个特定的键关联,称之为事件。当Lua对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下面。每个操作有一个相应的名称,代表了他的含义。他们在元表中的键是由名称前加上两条下划线;如,操作 "add" 的键是 "__add"。这些操作的语义

这里给出的Lua代码仅仅是说明性的;真正的行为是硬编码在解释器中的,比下面的的模拟的效率要高很多。描述中用到的函数 (rawget, tonumber, 等等) 在 5.1 中会对他们进行描述。特别地,要获得一个给定对象的元方法,我们使用这个表达式:

  metatable(obj)[event]

这个要读作:

  rawget(metatable(obj) or {}, event)



也就是,访问元方法时不会调用其它元方法,同时调用没有元表的对象不会出错(它返回一个 nil值)。

  • "add": + 加法操作。

    下面的 getbinhandler 函数定义了Lua如何给一个二元操作选择一个处理器。首先,Lua尝试第一个操作数。如果它的类型没有定义这个操作的处理器,那么然后Lua尝试第二个操作数。

     function getbinhandler (op1, op2, event)
    
    

    return metatable(op1)[event] or metatable(op2)[event]

    end

    利用该函数,op1 + op2 的行为方式可看作是

     function add_event (op1, op2)
    
    

    local o1, o2 = tonumber(op1), tonumber(op2)

    if o1 and o2 then – both operands are numeric?

     return o1 + o2   -- `+' here is the primitive `add'
    

    else – at least one of the operands is not numeric

     local h = getbinhandler(op1, op2, "__add")
    
     if h then
    
       -- call the handler with both operands
    
       return h(op1, op2)
    
     else  -- no handler available: default behavior
    
       error("...")
    
     end
    

    end

    end

  • "sub": - 操作。行为方式类似 "add" 操作。
  • "mul": * 操作。行为方式类似 "add" 操作。
  • "div": / 操作。行为方式类似 "add" 操作。
  • "pow": ^ (指数) 操作

     function pow_event (op1, op2)
    
    

    local o1, o2 = tonumber(op1), tonumber(op2)

    if o1 and o2 then – both operands are numeric?

     return __pow(o1, o2)   -- call global `__pow'
    

    else – at least one of the operands is not numeric

     local h = getbinhandler(op1, op2, "__pow")
    
     if h then
    
       -- call the handler with both operands
    
       return h(op1, op2)
    
     else  -- no handler available: default behavior
    
       error("...")
    
     end
    

    end

    end

  • "unm": 一元取负 - 操作。

     function unm_event (op)
    
    

    local o = tonumber(op)

    if o then – operand is numeric?

     return -o  -- `-' here is the primitive `unm'
    

    else – the operand is not numeric.

     -- Try to get a handler from the operand
    
     local h = metatable(op).__unm
    
     if h then
    
       -- call the handler with the operand and nil
    
       return h(op, nil)
    
     else  -- no handler available: default behavior
    
       error("...")
    
     end
    

    end

    end

  • "concat": .. (串连接)操作。

     function concat_event (op1, op2)
    
    

    if (type(op1) == “string” or type(op1) == “number”) and

      (type(op2) == "string" or type(op2) == "number") then
    
     return op1 .. op2  -- primitive string concatenation
    

    else

     local h = getbinhandler(op1, op2, "__concat")
    
     if h then
    
       return h(op1, op2)
    
     else
    
       error("...")
    
     end
    

    end

    end

  • "eq": == 操作。函数 getcomphandler 定义了Lua是如何为比较操作选择一个元方法的。只有当参与比较的两个对象属于同一类型而且需要的元方法一样时,才会选择这个元方法。

     function getcomphandler (op1, op2, event)
    
    

    if type(op1) ~= type(op2) then return nil end

    local mm1 = metatable(op1)[event]

    local mm2 = metatable(op2)[event]

    if mm1 == mm2 then return mm1 else return nil end

    end

    事件如下定义:

     function eq_event (op1, op2)
    
    

    if type(op1) ~= type(op2) then – different types?

     return false   -- different objects
    

    end

    if op1 == op2 then – primitive equal?

     return true   -- objects are equal
    

    end

    – try metamethod

    local h = getcomphandler(op1, op2, “__eq”)

    if h then

     return h(op1, op2)
    

    else

     return false
    

    end

    end

    a ~= b is equivalent to not (a == b).

  • "lt": < 操作。

     function lt_event (op1, op2)
    
    

    if type(op1) == “number” and type(op2) == “number” then

     return op1 &lt; op2   -- numeric comparison
    

    elseif type(op1) == “string” and type(op2) == “string” then

     return op1 &lt; op2   -- lexicographic comparison
    

    else

     local h = getcomphandler(op1, op2, "__lt")
    
     if h then
    
       return h(op1, op2)
    
     else
    
       error("...");
    
     end
    

    end

    end

    a > b is equivalent to b < a.

  • "le": <= 操作。

     function le_event (op1, op2)
    
    

    if type(op1) == “number” and type(op2) == “number” then

     return op1 &lt;= op2   -- numeric comparison
    

    elseif type(op1) == “string” and type(op2) == “string” then

     return op1 &lt;= op2   -- lexicographic comparison
    

    else

     local h = getcomphandler(op1, op2, "__le")
    
     if h then
    
       return h(op1, op2)
    
     else
    
       h = getcomphandler(op1, op2, "__lt")
    
       if h then
    
         return not h(op2, op1)
    
       else
    
         error("...");
    
       end
    
     end
    

    end

    end

    a >= b is equivalent to b <= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).

  • "index": 通过索引访问 table[key]

     function gettable_event (table, key)
    
    

    local h

    if type(table) == “table” then

     local v = rawget(table, key)
    
     if v ~= nil then return v end
    
     h = metatable(table).__index
    
     if h == nil then return nil end
    

    else

     h = metatable(table).__index
    
     if h == nil then
    
       error("...");
    
     end
    

    end

    if type(h) == “function” then

     return h(table, key)      -- call the handler
    

    else return h[key] – or repeat operation on it

    end

  • "newindex": 给表的索引赋值 table[key] = value

     function settable_event (table, key, value)
    
    

    local h

    if type(table) == “table” then

     local v = rawget(table, key)
    
     if v ~= nil then rawset(table, key, value); return end
    
     h = metatable(table).__newindex
    
     if h == nil then rawset(table, key, value); return end
    

    else

     h = metatable(table).__newindex
    
     if h == nil then
    
       error("...");
    
     end
    

    end

    if type(h) == “function” then

     return h(table, key,value)    -- call the handler
    

    else h[key] = value – or repeat operation on it

    end

  • "call": 当Lua调用某个值时调用。

     function function_event (func, ...)
    
    

    if type(func) == “function” then

     return func(unpack(arg))   -- primitive call
    

    else

     local h = metatable(func).__call
    
     if h then
    
       return h(func, unpack(arg))
    
     else
    
       error("...")
    
     end
    

    end

    end

2.9 - 垃圾收集

Lua 会自动进行内存管理。这意味着你不需要担心新对象的内存分配问题,也不需要释放不用的对象。Lua 通过不断地运行 垃圾收集器 收集 dead objects (也就是那些Lua中无法访问的对象)来自动管理内存。Lua中所有的对象都是自动管理的目标:表,用户数据,函数,线程,和字符串。Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。

通过C API,你可以查询和更改阀值(见 3.7)。将阀值设为零时会强制立刻进行垃圾收集,同时把他设为足够大就可以停止垃圾收集。仅使用Lua代码中的 gcinfocollectgarbage 函数 (见 5.1)可以获得一定程度上对垃圾收集循环的控制。

2.9.1 - 垃圾收集元方法 (Garbage-Collection Metamethods)

使用 C API,你可以对用户数据设置一个垃圾收集元方法(见 2.8)。这些元方法也称为 终结器(finalizers)。终结器允许你用外部的资源管理来调整Lua的垃圾收集(如关闭文件,网络或数据库连接,或者释放你自己的内存。

用元表中包含 __gc 字段的自由用户数据不会立即被垃圾收集器回收。而是,Lua把它们放在一个列表中。收集完毕之后,Lua会对这个列表中的用户数据执行和以下函数相等的操作:

 function gc_event (udata)

local h = metatable(udata).__gc

if h then

 h(udata)

end

end

在每个垃圾收集过程最后,调用用户数据的终结器的顺序,将按照他们在收集过程中添加到列表中的相反顺序进行。也就是,第一个被调用的终结器是和在程序中创建的最后一个用户数据相关的那个终结器。

2.9.2 - 弱表

一个 弱表(weak table) 是一个包含的元素是 弱引用(weak references)的表。垃圾收集器会忽略弱引用。换句话说,如果指向一个对象的引用只有弱引用,那么这个对象还是要被垃圾收集器回收。

弱表可以包含弱的键,弱的值,或者两者皆有。一个包含弱键的表允许它的键被回收,但值不可以。一个同时包含弱键和弱值的表允许键和值的回收。无论哪种情况,只要键或者值中的一个被回收了,那么这一对键值将会从表中删除。这个表的弱属性是由它的元表的 __mode 字段控制的。如果 __mode 字段是一个包含字符 `k´的字符串,那么表中的键是弱键。如果 __mode 字段是一个包含字符 `v´ 的字符串,那么表中的值是弱值。

在你将表用作元表之后,你不应该更改 __mode 字段的值。否则,这个元表控制的表的弱表行为将会不确定。

2.10 - 同步程序

Lua支持同步程序,也称为 半同步程序(semi-coroutines)协同多线程(collaborative multithreading)。Lua中的一个同步程序代表了一个独立的执行线程。然而,不像在多线程系统中的线程那样,一个同步程序只有在调用了一个yield(产生结果)函数才能挂起它的执行。

你可以调用 coroutine.create 来创建一个同步程序。它唯一的一个参数是一个函数,代表同步程序的主函数。create 函数仅仅建立一个新的同步程序然后返回一个它的句柄 (一个线程 thread 类型的对象);它不会启动该同步程序。

当你第一次调用 coroutine.resume,将 coroutine.create 返回的线程对象作为第一个参数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给 coroutine.resume 的额外的参数会作为同步程序主函数的参数传递过去。在同步程序开始执行之后,它一直运行到它结束或产生结果。

一个同步程序通过两种方式结束它的运行:正常情况下,当它的主函数返回(显式地或隐式的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,coroutine.resume 返回 true,加上同步程序主函数返回的其它值。在有错误的情况下,coroutine.resume 返回 false ,并附上错误信息。

一个同步程序通过调用 coroutine.yield 来产生结果。当一个同步程序产生结果,相应的 coroutine.resume 就立刻返回,即使操作发生在嵌套函数调用中(也就是,不在主函数中,而在被主函数直接或间接调用的函数中)。在这种情况下, coroutine.resume 也返回 true,以及传给 coroutine.yield。的所有参数。下次你继续同一个同步程序时,它会从它原来yield的地方继续执行,而 coroutine.yield 将返回给主程序传给 coroutine.resume 的额外参数。

coroutine.wrap 函数创建一个和 coroutine.create 一样的同步程序,但它不返回同步程序本身,而是返回一个继续同步程序的函数(当调用的时候)。传递给这个函数的参数作为继续resume的额外参数。函数将返回resume返回的所有值,出除了第一个(布尔值的错误代码)。不像 coroutine.resume,这个函数不捕获错误;出现任何错误都传回给调用者。

请考虑以下例子:

function foo1 (a)

print(“foo”, a)

return coroutine.yield(2*a)

end

co = coroutine.create(function (a,b)

  print("co-body", a, b)

  local r = foo1(a+1)

  print("co-body", r)

  local r, s = coroutine.yield(a+b, a-b)

  print("co-body", r, s)

  return b, "end"

end)

a, b = coroutine.resume(co, 1, 10)

print(“main”, a, b)

a, b, c = coroutine.resume(co, “r”)

print(“main”, a, b, c)

a, b, c = coroutine.resume(co, “x”, “y”)

print(“main”, a, b, c)

a, b = coroutine.resume(co, “x”, “y”)

print(“main”, a, b)

当你运行它的时候,它会产生以下输出结果:

co-body 1       10

foo 2

main true 4

co-body r

main true 11 -9

co-body x y

main true 10 end

main false cannot resume dead coroutine

3 - 应用程序接口

这一节描述Lua中的C API,这是对于宿主程序可用的C函数集合,用以和Lua通讯。所有的API函数及其相关类型和常量都声明在头文件lua.h中。

即便每次我都使用“函数”这个词,任何设施在API里面都可能被一个宏所替代。所有这些宏(macro)都只使用一次它的参数(除了第一个参数、这个每次总是一个Lua状态),所以不会产生隐藏的副作用。

3.1 - 状态

Lua库是可重入的(reentrant)的:它没有全局变量。整个Lua解释器的状态(全局变量、栈、等等)储存在一个动态分配的 lua_State 结构类型中。一个指向这个状态的指针必须作为库中每一个函数的第一个参数,除了 lua_open 这个函数。该函数从最开始创建一个Lua状态。

在调用任何API函数之前,你必须通过调用 lua_open 创建一个状态:

       lua_State *lua_open (void);

调用 lua_close 去释放这个由 lua_open 创建的状态:

       void lua_close (lua_State *L);

这个函数销毁所有被给予Lua状态的对象(调用相应的垃圾收集元方法)并且释放那个状态使用的所有动态内存。在个别的平台上,你或许不需要调用这个函数,因为当宿主程序结束的时候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守护进程或者Web服务器,可能需要立即释放那些不需要的状态资源,以避免占用太多内存。

3.2 - 堆栈和索引

Lua使用一个来自于C语言的 虚拟栈(virtual stack) 传递值。栈里面的每一个元素都代表一个Lua值 (nil, number, string, etc.)。

只要Lua调用C语言函数,这个所调用的函数将得到一个新的栈,这个栈将独立于先前的栈以及那些仍然活跃的C函数的栈。这个栈最初包含了C函数的所有参数,并且这也会存放C函数的返回值(见 3.16)。

为了方便起见,大多数查询操作的API不需要遵守一个严格的栈定义(注:即不需要遵循FILO)。他们可以使用 索引(index) 引用任何栈中元素:一个正数索引代表了栈中的绝对位置(从1开始);一个负数索引代表了从栈顶的偏移量。更特别的是,如果栈有 n 个元素,那么索引 1 代表第一个元素(这就是说,这个元素首先入栈)并且索引 n 代表了最后一个元素;索引 -1 也代表了最后一个元素(也就是栈顶)并且索引 -n 代表了第一个元素。我们说一个索引存在于 1 和栈顶之间是有效的,换句话说,如果 1 <= abs(index) <= top

在任何时间里,你可以调用 lua_gettop 得到栈顶元素的索引:

       int lua_gettop (lua_State *L);

因为索引从 1 开始,lua_gettop 的结果等于栈中的元素数量(如果是0就意味着栈为空)。

当你与Lua API交互的时候,你有责任控制堆栈以避免溢出。。这个函数

       int lua_checkstack (lua_State *L, int extra);

使栈的大小增长为 top + extra 个元素;如果无法将栈增加到那个大小将返回false。这个函数从不对栈进行收缩;如果栈已经比新的大小更大,它将不产生任何作用那个。

只要Lua调用C 函数,它必须至少保证 LUA_MINSTACK 这个栈中的位置是可用的。LUA_MINSTACK 定义在 lua.h 中,它的值是 20,所以你不需要总担心栈空间除非你的代码通过循环将元素压入栈。

大多数插叙函数接受指向有效栈空间的索引,那就是说,索引达到栈空间的最大值是你需要使用 lua_checkstack。这样的索引称为可接受索引(acceptable indices)。更正规的说法,我们给出一个严格的定义如下:

     (index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)



注意,0永远不是一个可接受索引。

除非另外说明,任何函数接受有效索引可以被称为是 伪索引(pseudo-indices),这些索引代表一些Lua值可以被C 代码访问但是却不存在于栈中。假索引通常用于访问全局环境变量,注册表,和一个C 函数的上值(见 3.17)。

3.3 - 堆栈操作

一下的API提供了基本的栈操作:

       void lua_settop    (lua_State *L, int index);

   void lua_pushvalue (lua_State *L, int index);

   void lua_remove    (lua_State *L, int index);

   void lua_insert    (lua_State *L, int index);

   void lua_replace   (lua_State *L, int index);

lua_settop 接受任何可接受的索引,或者0,并且将该索引设置为栈顶。如果新的栈顶比旧的更大,那么新元素被填上 nil 值。如果索引为 0,那么所有栈元素会被清除。在 lua.h 里面定义了一个有用的宏

       #define lua_pop(L,n)   lua_settop(L, -(n)-1)

用以从栈中弹出 n 个元素。

lua_pushvalue 将一个索引指向的元素的拷贝压入栈。 lua_remove 删除指定位置的元素,将该元素上方的所有元素下移以填满空缺。lua_insert 将栈顶元素移动到指定位置,将该位置以上的元素上移。lua_replace 将栈顶元素移动到指定位置而不移动其他任何其他元素(因此替代了给定位置的元素的值)。所有这些函数只接受有效的索引。(你不能使用伪索引调用 lua_removelua_insert,因为他们不代表栈中的位置。)

举个例子,如果栈开始于 10 20 30 40 50*(自底向上;`*´ 标记了栈顶),那么:

       lua_pushvalue(L, 3)    --> 10 20 30 40 50 30*

   lua_pushvalue(L, -1)   --&gt; 10 20 30 40 50 30 30*

   lua_remove(L, -3)      --&gt; 10 20 30 40 30 30*

   lua_remove(L,  6)      --&gt; 10 20 30 40 30*

   lua_insert(L,  1)      --&gt; 30 10 20 30 40*

   lua_insert(L, -1)      --&gt; 30 10 20 30 40*  (no effect)

   lua_replace(L, 2)      --&gt; 30 40 20 30*

   lua_settop(L, -3)      --&gt; 30 40*

   lua_settop(L,  6)      --&gt; 30 40 nil nil nil nil*

3.4 - 堆栈查询

下面的函数可以用来检测栈内元素的类型:

       int lua_type            (lua_State *L, int index);

   int lua_isnil           (lua_State *L, int index);

   int lua_isboolean       (lua_State *L, int index);

   int lua_isnumber        (lua_State *L, int index);

   int lua_isstring        (lua_State *L, int index);

   int lua_istable         (lua_State *L, int index);

   int lua_isfunction      (lua_State *L, int index);

   int lua_iscfunction     (lua_State *L, int index);

   int lua_isuserdata      (lua_State *L, int index);

   int lua_islightuserdata (lua_State *L, int index);

这些函数只能使用可接受的索引。

lua_type 返回栈中元素值的类型,如果所有索引无效则返回 LUA_TNONE(就是说如果栈为空)。这些lua_type 代表的返回值作为常量定义在 lua.h 中:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA。下面的函数将这些常量转换成字符串:

       const char *lua_typename  (lua_State *L, int type);

lua_is* 函数返回 1 当对象与所给类型兼容的时候,其他情况返回 0。 lua_isboolean 是一个例外:它只针对布尔值时才会成功(否则将是无用的,因为任何值都是一个布尔值)。这些函数对于无效引用返回 0。 lua_isnumber 接受数字和用数字表示的字符串;lua_isstring 接受字符串和数字(见 2.2.1);lua_isfunction 接受Lua函数和C函数; lua_isuserdata 接受完整的和轻量的用户数据。要区分C 函数和Lua 函数,你可以使用 lua_iscfunction。要区分用户数据,你可以使用 lua_islightuserdata。要区分数字还是用数字表示的字符串,你可以使用 lua_type

这些API还包含了用于比较栈中的两个值的操作:

       int lua_equal    (lua_State *L, int index1, int index2);

   int lua_rawequal (lua_State *L, int index1, int index2);

   int lua_lessthan (lua_State *L, int index1, int index2);

lua_equallua_lessthan 在比较他们的副本的时候是等效的(见 2.5.2)。 lua_rawequal 用于比较基本类型但不包括元方法。如果有任何形式的无效索引,这些函数都返回 0(false)。

3.5 - 堆栈取值

为了将一个栈中的值转变为指定的C语言类型,你需要使用以下的转换函数:

       int            lua_toboolean   (lua_State *L, int index);

   lua_Number     lua_tonumber    (lua_State *L, int index);

   const char    *lua_tostring    (lua_State *L, int index);

   size_t         lua_strlen      (lua_State *L, int index);

   lua_CFunction  lua_tocfunction (lua_State *L, int index);

   void          *lua_touserdata  (lua_State *L, int index);

   lua_State     *lua_tothread    (lua_State *L, int index);

   void          *lua_topointer   (lua_State *L, int index);

这些函数由任何可接受索引作为参数进行调用。当遇到一个无效索引,函数表现为就好像接受了一个错误类型的值。

lua_toboolean 将索引指向的Lua值转换为C语言类型的布尔值(0 或 1)。就像所有Lua中的测试一样,任何不等于 false 或者 nil 的Lua值通过 lua_toboolean 都将返回 1;否则将返回 0。当然,如果是一个无效索引,也将返回 0。(如果你只想接受真实的布尔值,使用 lua_isboolean 去测试值的类型。)

lua_tonumber 将索引指向的Lua值转换成一个数字(默认情况下,lua_Numberdouble类型)。Lua值必须是一个数字或者可转化为数字的字符串(见 2.2.1);否则,lua_tonumber 返回 0。

lua_tostring 将索引指向的Lua值转换成字符串(const char*)。Lua值必须是一个字符串或者数字;否则,函数返回 NULL。如果值是一个数字,lua_tostring 会将栈中的真实值变成一个字符串类型。(当 lua_tostring 应用于键时这个改变将引起 lua_next 的混乱。)lua_tostring 在Lua 状态内部返回一个字符串的指针。这个字符串总是以 0('\0')结尾,就像C 语言里的一样,但是也可能包含其他 0 在其中。如果你不知道一个字符串中是否存在 0 ,你可以使用 lua_strlen 得到它的实际长度。因为Lua具有垃圾收集机制,所以不能保证 lua_tostring 返回的指针仍然有效,当相应的值从栈中删除之后。如果你在当前函数返回之后还需要这个字符串,你需要复制它并且将它存入注册表(见 3.18)。

lua_tocfunction 将栈中的值转换为C 函数。这个值必须是一个C 函数;否则,lua_tocfunction 返回 NULL。类型 lua_CFunction3.16 中有详细解释。

lua_tothread 将栈中的值转换为Lua线程(被描绘成 lua_State *)。这个值必须是一个线程;否则;lua_tothread 返回 NULL

lua_topointer 将栈中?闹底晃ㄓ玫腃 语言指针(void *)。这个值可能是一个用户数据、表、线程、或者函数;否则,lua_topointer 返回 NULL。Lua保证同种类型的不同对象将返回不同指针。没有直接的方法将指针转换回原来的值。这个函数通常用于调试。

lua_touserdata3.8 中有详细解释。

3.6 - 将值压入堆栈

以下的API函数将C 语言值压入栈:

       void lua_pushboolean       (lua_State *L, int b);

   void lua_pushnumber        (lua_State *L, lua_Number n);

   void lua_pushlstring       (lua_State *L, const char *s, size_t len);

   void lua_pushstring        (lua_State *L, const char *s);

   void lua_pushnil           (lua_State *L);

   void lua_pushcfunction     (lua_State *L, lua_CFunction f);

   void lua_pushlightuserdata (lua_State *L, void *p);

这些函数接受一个C 语言值,将其转换成相应的Lua 值,并且将结果压入栈。需要特别注意的是,lua_pushlstringlua_pushstring 将对所给的字符串做一个内部拷贝。lua_pushstring 只能压入合适的C 语言字符串(也就是说,字符串要以 '\0' 结尾,并且不能包含内嵌的 0);否则,你需要使用更通用的 lua_pushlstring 函数,它可以接受一个指定的大小。

你可以压入“格式化的”字符串:

       const char *lua_pushfstring  (lua_State *L, const char *fmt, ...);

   const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);

这些函数将格式化的字符串压入栈并且返回这个字符串的指针。它们和 sprintfvsprintf 类似,但是有一些重要的不同之处:

  • 你不需要为结果分配空间:结果是Lua字符串并且Lua会关心内存分配问题(和内存释放问题,通过垃圾收集机制)。
  • 转换受到限制。这里没有标志、宽度或精度。转换操作的修饰符可以是简单的`%%´(在字符串中插入一个`%´),`%s´(插入一个没有大小限制的以 0 结尾的字符串),`%f´(插入一个 lua_Number),`%d´(插入一个 int),`%c´(插入一个 int 作为一个字符)。

这个函数

       void lua_concat (lua_State *L, int n);

连接栈顶的 n 个值,将它们弹出,并且将结果留在栈顶。如果 n 为 1,结果是单个字符串(也就是说,函数什么也不做);如果 n 是 0,结果是空字符串。连接的完成依据Lua的语义(见 2.5.4)。

3.7 - 控制垃圾收集

Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。(见 2.9)。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。

你可以通过以下的函数得到这两个量的当前值:

       int  lua_getgccount     (lua_State *L);

   int  lua_getgcthreshold (lua_State *L);

它们的返回值的单位都是千字节(K bytes)。你可以通过下面的函数改变阀值

       void  lua_setgcthreshold (lua_State *L, int newthreshold);



然后,新的阀值得单位也是千字节。当你调用这个函数,Lua设置阀新值并且和字节计数器作比较。如果新的阀值小于字节计数器,Lua将立刻运行垃圾收集器。特别是 lua_setgcthreshold(L,0) 强迫进行垃圾收集。在这之后,一个新值根据先前的规则被设置。

3.8 - 用户数据类型 (Userdata)

用户数据代表了Lua中使用的C语言值。Lua支持两种用户数据:完整用户数据(full userdata)轻量用户数据(light userdata)

一个完整用户数据代表了一块内存。它是一个对象(像一个表):你必须创建它,它有自己的元表,当它被回收的时候你可以检测到。一个完整用户数据只能与自己相等(基于原始的相等规则)。

一个轻量用户数据代表一个指针。它是?桓鲋担ㄏ褚桓鍪郑耗悴⒚挥写唇ㄋ裁挥性怼ⅲ荒鼙换厥眨ㄒ蛭游幢淮唇ǎG崃坑没菹嗟鹊奶跫侵刚胫赶虻牡刂废嗤?

在Lua 代码里,没办法测试用户数据类型是完整的还是轻量的;两者都是 用户数据类型。在C 代码里,如果是完整用户数据,lua_type 返回 LUA_TUSERDATA,反之,返回 LUA_TLIGHTUSERDATA

你可以通过下面的函数创建完整用户数据:

       void *lua_newuserdata (lua_State *L, size_t size);

这个函数根据指定大小分配一个内存块,将用户数据的地址压入栈并且返回这个地址。

要将轻量用户数据压入栈,你需要使用 lua_pushlightuserdata(见 3.6)。

lua_touserdata (见 3.5)用来取回用户数据的值。当你用在完整用户数据的时候,它返回这个块的地址,当你用在轻量用户数据的时候,它返回它的指针,当你用在非用数据的时候,返回 NULL

当Lua回收一个完整用户数据,它调用该用户数据的 gc 元方法,然后释放该用户数据相应的内存。

3.9 - 元表 (Metatables)

下面的函数允许你操作对象的元表:

       int lua_getmetatable (lua_State *L, int index);

   int lua_setmetatable (lua_State *L, int index);

lua_getmetatable 将所给对象的元表压入栈。如果索引无效,或这个对象不含有元表,该函数返回 0 并且不对栈进行任何操作。

lua_setmetatable 从栈中弹出一张表并且为所给对象设置一个新的元表。当无法给所给对象设置元表的时候该函数返回 0(也就是说,这个对象既不是一个用户数据也不是一张表);尽管那样,它仍从栈中弹出这张表。

3.10 - 加载Lua语句段

你可以通过 lua_load 加载一个Lua块:

       typedef const char * (*lua_Chunkreader)

                            (lua_State *L, void *data, size_t *size);



   int lua_load (lua_State *L, lua_Chunkreader reader, void *data,

                               const char *chunkname);

lua_load 的返回值是:

  • 0 --- 没有错误
  • LUA_ERRSYNTAX --- 预编译时句法错误
  • LUA_ERRMEM --- 内存分配错误

如果没有错误,lua_load 将编译过的语句段作为Lua 函数压入栈顶。否则,它将压入一个错误信息。

lua_load 自动检测语句段的类型是文本还是二进制数据,并且根据类型将其载入(见程序 luac)。

lua_load 使用一个用户提供的 reader 函数读取语句段的内容。当需要调用其它段时,lua_load 调用 reader,传递其 data 参数。必须返回指向语句段所在的新内存块的指针,并将段大小设置为 0。为了标志块尾,reader 必须返回 NULL。reader 函数可以返回任何大于零的值。

在当前的实现中,reader 函数不能调用任何Lua 函数;为了保证这一点,它总是会得到为 NULL 的Lua状态。

语句段名(chunkname) 用于错误信息和调试信息(见 4)。

参考辅助库 (lauxlib.c) 了解如何使用 lua_load 以及如何使用现成的函数从文件和字符串中加载语句段。

3.11 - 表操作

通过调用以下函数可以创建表:

       void lua_newtable (lua_State *L);

这个函数创建一张新的空表,并将其压入栈。

要从栈中的表里读取值,使用:

       void lua_gettable (lua_State *L, int index);

index 代表表的位置。lua_gettable 从栈中弹出一个键,并且返回该键对应的值,表仍然留在堆栈中。在Lua中,这个函数可能触发一个针对 index 事件的元方法(见 2.8)。想要在不调用任何元方法的情况下得到表主键所对应的真实值,使用这个原始(raw)版本:

       void lua_rawget (lua_State *L, int index);

要将一个值储存到栈中的一张表中,你需要将键压入栈,再将值压入栈,调用:

       void lua_settable (lua_State *L, int index);



index 代表表的位置。lua_settable 从栈中弹出主键和值。表仍然留在栈中。在Lua中,这个操作可能触发针对 settable 或者 newindex 事件的元方法。想要不受这些元方法的影响并且为任意表设置值,使用这个原始(raw)版本:

       void lua_rawset (lua_State *L, int index);

你可以通过这个函数遍历一张表:

       int lua_next (lua_State *L, int index);

index 指向需要被遍历的表。这个函数从堆栈中弹出一个键,从表中取一对键-值压入栈(所给键的下一对)。如果没有更多的元素,lua_next 返回 0(对栈不进行操作)。使用一个 nil 键标示遍历的开始。

一个典型的遍历操作看起来像这样:

       /* table is in the stack at index `t' */

   lua_pushnil(L);  /* first key */

   while (lua_next(L, t) != 0) {

     /* `key' is at index -2 and `value' at index -1 */

     printf("%s - %s\n",

       lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));

     lua_pop(L, 1);  /* removes `value'; keeps `key' for next iteration */

   }

当遍历一张表的时候,不要在键上直接调用 lua_tostring,除非你知道这个键确实是一个字符串。再次调用 lua_tostring 改变了所给索引指向的值;这使 lua_next 的调用发生混乱。

3.12 - 环境变量操作 (Manipulating Environments)

所有的全局变量保存在普通的Lua 表中,叫做环境变量。初始的环境变量被称作全局环境变量。这张表总是在 LUA_GLOBALSINDEX 这个伪索引处。

要访问或改变全局变量的值,你可以对环境变量表使用常规的表操作。举个例子,存取一个全局变量的值:

       lua_pushstring(L, varname);

   lua_gettable(L, LUA_GLOBALSINDEX);

你可以改变一个Lua 线程的全局环境变量通过 lua_replace 函数。

以下的函数提供获取、设置Lua函数的环境变量的功能:

       void lua_getfenv (lua_State *L, int index);

   int  lua_setfenv (lua_State *L, int index);

lua_getfenv 将堆栈中 index 索引指向的函数的环境变量表压入栈。如果函数是一个C 函数,lua_getfenv 将全局环境变量压入栈。lua_setfenv 从栈中弹出一张表并且将其设置为栈中 index 索引处的函数的新环境变量。如果给定索引处的对象不是一个Lua 函数,lua_setfenv 返回 0。

3.13 - 将表作为数组使用 Using Tables as Arrays

有一些 API 能够帮助我们将Lua 表作为数组使用,也就是说,表只由数字作为索引:

       void lua_rawgeti (lua_State *L, int index, int n);

   void lua_rawseti (lua_State *L, int index, int n);

lua_rawgeti 将表中的第 n 个元素放入堆栈中的指定位置 indexlua_rawseti 将堆栈中指定位置 index 处的表中的第 n 个元素的值设定为栈顶的值,并将原来的值从栈中删除。

3.14 - 调用函数

定义在Lua 中的函数和C语言函数经过注册就可以被宿主程序调用。这些调用必须遵循以下协议:首先,被调用的函数被压入栈;然后,函数的参数必须顺序(direct order)输入,也就是说,第一个参数需要被第一个输入。最后,函数通过下面的方法调用:

       void lua_call (lua_State *L, int nargs, int nresults);

nargs 是你压入栈的参数的数量。所有参数和函数值从堆栈中弹出,并且函数结果被压入栈。返回值的数量被调整为 nresults,除非 nresultsLUA_MULTRET。在那种情况下,所有函数结果都被压入栈。Lua 会检测返回值是否适合栈空间。函数返回值按顺序被压入栈(第一个返回值首先入栈),所以调用结束后最后一个返回值在栈顶。

下面的例子展示宿主程序如何可以和这个Lua 代码等效:

       a = f("how", t.x, 14)

这里是C 语言里的做法:

    lua_pushstring(L, "t");

lua_gettable(L, LUA_GLOBALSINDEX);          /* global `t' (for later use) */

lua_pushstring(L, "a");                                       /* var name */

lua_pushstring(L, "f");                                  /* function name */

lua_gettable(L, LUA_GLOBALSINDEX);               /* function to be called */

lua_pushstring(L, "how");                                 /* 1st argument */

lua_pushstring(L, "x");                            /* push the string "x" */

lua_gettable(L, -5);                      /* push result of t.x (2nd arg) */

lua_pushnumber(L, 14);                                    /* 3rd argument */

lua_call(L, 3, 1);         /* call function with 3 arguments and 1 result */

lua_settable(L, LUA_GLOBALSINDEX);             /* set global variable `a' */

lua_pop(L, 1);                               /* remove `t' from the stack */

注意上面的代码是“平衡的”:在它结束时,堆栈返回原来的配置。这个被认为是良好的编程实践。

(为了展示细节,我们只用Lua 提供的原始 API 完成这个例子。通常程序员定义并使用几个宏和辅助库函数在Lua 中提供高级存取功能。请参考例子中标准库函数的源代码。)

3.15 - 受保护调用 Protected Calls

当你通过 lua_call 调用一个函数,所调用函数内部产生的错误将向上传递(通过一个 longjmp)。如果你需要处理错误,你应该使用 lua_pcall

       int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

nargsnresultslua_call 中有相同的意义。如果调用过程中没有错误,lua_pcall 的行为非常像 lua_call 。然而,如果有错误,lua_call 会捕获它,将一个单一值(错误信息)压入栈,并且返回错误代码。像 lua_calllua_pcall 总是从栈中删除函数和它的参数。

如果 errfunc 是 0,所返回的错误信息就是原始的错误信息。另外,errfunc 给出一个指向错误处理函数(error handler function)的栈索引。(在当前的实现中,索引不能为伪索引。)假设运行时错误,函数将和错误信息一起被调用,并且他的返回值将是 lua_pcall 返回的信息。

错误处理函数被用来为错误信息增加更多的调试信息,例如栈的记录。这样的信息在 lua_pcall 调用返回后将不能被收集,因此栈已经被解开了。

如果 lua_pcall 函数调用成功返回 0,否则返回以下的一个错误代码(定义在 lua.h):

  • LUA_ERRRUN --- 运行时错误
  • LUA_ERRMEM --- 内存分配错误。这样的错误下,Lua 不调用错误处理函数
  • LUA_ERRERR --- 运行错误处理函数时发生的错误

3.16 - 定义C 函数

Lua可以通过C 语言写的函数进行扩展,这些函数必须是 lua_CFunction 类型的,作为以下定义:

       typedef int (*lua_CFunction) (lua_State *L);

一个C 函数接收一个Lua 状态并且返回一个整数,数值需要返回给Lua。

为了正确的和Lua 通讯,C 函数必须遵循以下协议,它定义了参数和返回值传递的方法:一个C 函数在它的堆栈中从Lua获取顺序(第一个参数首先入栈)参数。所以,当函数开始时,第一个参数在索引位置 1。为了将返回值传递给Lua,一个C 函数将它们顺序压入栈,并且返回它们的数量。任何在堆栈中位于返回值以下的值都将被Lua 适当的解除。就像Lua 函数一样,一个C 函数被Lua 调用也可以返回很多结果。

作为一个例子,下面的函数接收一个任意数量的数字参数并且返回它们的平均值和总合:

       static int foo (lua_State *L) {

     int n = lua_gettop(L);    /* number of arguments */

     lua_Number sum = 0;

     int i;

     for (i = 1; i &lt;= n; i++) {

       if (!lua_isnumber(L, i)) {

         lua_pushstring(L, "incorrect argument to function `average'");

         lua_error(L);

       }

       sum += lua_tonumber(L, i);

     }

     lua_pushnumber(L, sum/n);        /* first result */

     lua_pushnumber(L, sum);         /* second result */

     return 2;                   /* number of results */

   }

下面是一些便利的宏用来在Lua中注册一个C 函数:

       #define lua_register(L,n,f) \

           (lua_pushstring(L, n), \

            lua_pushcfunction(L, f), \

            lua_settable(L, LUA_GLOBALSINDEX))

 /* lua_State *L;    */

 /* const char *n;   */

 /* lua_CFunction f; */

它接收Lua 中的函数名和一个指向函数的指针。这样,上面的C 函数foo可以在Lua中被注册为 average 并被调用。

       lua_register(L, "average", foo);

3.17 - 定义C 函数关闭 Defining C Closures

当一个C 函数被创建后,它可以与一些值关联,这样创建了一个 C 闭包(C closure);这些值可以被随时被函数访问。为了使值和C 函数关联,首先这些值要被压入栈(有多个值时,第一个值先入),然后这个函数

       void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);



被用来将C 函数压入栈,通过参数 n 告知应该有多少个值和该函数关联(lua_pushcclosure 将这些值从堆栈中弹出);事实上,这个宏 lua_pushcfunction 被定义作为 lua_pushcfunctionn 设置为 0。

然后,无论何时C 函数被调用,那些值被定为于指定的伪索引处。那些伪索引有一个宏 lua_upvalueindex 产生。第一个和函数关联的值在 lua_upvalueindex(1) 处,其他的以此类推。当 n 比当前函数的上值大时,lua_upvalueindex(n) 会产生一个可接受的索引(但是无效)。

C语言函数和关闭的例子,请参考Lua官方发行版中的标准库(src/lib/*.c)。

3.18 - 注册表 Registry

Lua提供了一个注册表,一张可以被所有C 代码用来储存任何需要储存的Lua值的预定义表,特别是如果C 代码需要维护C 函数以外存活的Lua值。这张表总是位于 LUA_REGISTRYINDEX 这个为索引处。任何C 语言库可以将数据储存在这张表中,只要它选择的键和其他库不同。典型的做法是你应该使用字符串作为主键包含你的库名或者在你的代码中使用一个包含C 对象地址的轻量用户数据。

在注册表中的整数键被引用机制所使用,由辅助库实现,因此不应该被用作其它用途。

3.19 - C 中的错误处理 Error Handling in C

总的来说,Lua使用C longjmp 机制来处理错误。当Lua面对任何错误(例如内存分配错误,类型错误,句法错误)它 升起(raises) 一个错误,也就是说,它做了一个长跳跃。一个受保护的环境使用 setjmp 设置一个恢复点;任何错误跳至最近最活跃的恢复点。

如果错误发生在任何受保护的环境,Lua调用 panic 函数 并且随后调用exit(EXIT_FAILURE)。你可以将panic 函数变为以下内容。

       lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);

你的新panic 函数可以避免程序因为没有返回(例如通过一个长跳跃)而退出。否则,相应的Lua 状态将不一致;唯一安全的操作就是关闭它。

几乎所有的API 函数都可能引起错误,例如导致一个内存分配错误。:lua_open, lua_close, lua_loadlua_pcall 这些的函数运行在保护模式下(也就是说,它们创建了一个受保护的环境并在其中运行),所以它们从不会引起错误。

有另外一个函数将所给的C 函数运行在保护模式下:

       int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);

lua_cpcall 在保护模式下调用 funcfunc 由一个包含 ud 的轻量用户数据开始。在错误问题上,lua_cpcalllua_pcall 一样返回相同的错误代码(见 3.15),加上在栈顶的一个错误对象;否则,返回 0,并且不对堆栈进行任何操作。任何由 func 返回的值都被丢弃。

C 代码可以通过调用下面的函数产生一个Lua错误:

       void lua_error (lua_State *L);

错误信息(实际上可以是任何类型的对象)必须在栈顶。这个函数进行一个长跳跃,因此从来不会返回。

3.20 - 线程

Lua 提供了操作线程的部分支?帧H绻阌卸嘞叱滩僮鞯腃 语言库,那么Lua能够与其协作并且在Lua中实现相同的机制。同样,Lua在线程之上实现自己的协同程序系统。以下函数用来在Lua中创建一个线程:

       lua_State *lua_newthread (lua_State *L);

这个函数将线程压入栈并且返回代表新线程的 lua_State 指针。这个返回的新状态与所有全局对象(例如表)共享初始状态,但是有一个独立的运行时堆栈。

每个线程都有自己独立的全局环境表。当你创建一个线程,这张表就和所给状态一样,但是你可以独自更改它们。

没有明确的函数可以关闭或者销毁一个线程。线程和其他Lua对象一样受垃圾收集程序的支配:

要像协同程序一样操作线程,Lua提供了以下函数:

       int lua_resume (lua_State *L, int narg);

   int lua_yield  (lua_State *L, int nresults);

你需要创建一个线程以便启动协同程序;然后你将函数体和事件参数压入堆栈;然后调用 lua_resumenarg 的值代表参数的数量。当同步程序暂停或者结束执行,函数将返回。当它返回后,栈中包含的所有值传递给 lua_yield,或者有主体函数返回。如果同步程序运行无误,lua_resume 返回 0,否则返回一个错误代码(见 3.15)。对于错误,堆栈只包含错误信息。要重起同步程序,将作为结果传递给 yield 的值压入堆栈,并且调用 lua_resume

lua_yield 函数只能像C 函数的返回表达式一样被调用,就像下面所展示的:

       return lua_yield (L, nresults);

如果C 函数像这样调用 lua_yield,正在运行的同步程序暂停它的执行,并且调用 lua_resume 开始让这个协同程序返回。nresults 这个参数代表了在堆栈中作为结果传递给 lua_resume 的值的数量。

要在不同线程中交换值,你可以使用 lua_xmove

       void lua_xmove (lua_State *from, lua_State *to, int n);

它从堆栈 from 中弹出 n 个值,并将其压入堆栈 to

4 - 调试接口 The Debug Interface

Lua 没有内置的调试设施。它使用一种特殊的接口,这种接口依赖函数和 钩子(hooks)。该接口允许构造不同种类的调试器,分析器以及其他工具用以从解释器得到所需的信息。

4.1 - 堆栈及函数信息 Stack and Function Information

得到解释程序运行时堆栈信息的主要函数是:

       int lua_getstack (lua_State *L, int level, lua_Debug *ar);

这个函数用一个指定等级的函数的 activation record 的标示符填充一个 lua_Debug 结构,等级 0 是当前运行函数,然而等级 n+1 是在等级 n 上调用的函数。当没有错误发生时,lua_getstack 返回 1;当在比栈更深的等级上调用的时候,它返回 0;

lua_Debug 结构被用来携带一个处于活动状态的函数的各种信息:

      typedef struct lua_Debug {

    int event;

    const char *name;      /* (n) */

    const char *namewhat;  /* (n) `global', `local', `field', `method' */

    const char *what;      /* (S) `Lua' function, `C' function, Lua `main' */

    const char *source;    /* (S) */

    int currentline;       /* (l) */

    int nups;              /* (u) number of upvalues */

    int linedefined;       /* (S) */

    char short_src[LUA_IDSIZE]; /* (S) */



    /* private part */

    ...

  } lua_Debug;

lua_getstack 只填充结构的私有部分以备之后使用。要填充 lua_Debug 其他有用信息,调用

       int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);



这个函数发生错误是返回 0 (举个例子,一个无效的 what 选项)。what 字符串中的每个字符选择填充一些 ar 结构的字段,把上面在 lua_Debug 定义中用圆括号括起来的字母作为指示: `S´ 填充在 source, linedefinedwhat 字段中;`l´ 填充在 currentline 字段中,等等...。而且,`f´ 将正在运?性谒燃渡系暮谷攵颜弧?

想要从一个不处于活动状态的函数那得到信息(就是不在栈上的函数),你需要将其压入栈并且用 >´ 作为 what 字符串的开始。举个例子,要知道函数 f 定义在第几行,你需要这样写

       lua_Debug ar;

   lua_pushstring(L, "f");

   lua_gettable(L, LUA_GLOBALSINDEX);  /* get global `f' */

   lua_getinfo(L, "&gt;S", &amp;ar);

   printf("%d\n", ar.linedefined);

lua_Debug 的字段有如下的含义:

  • source 如果函数在一个字符串中定义,那么 source 就是那个字符串。如果函数定义在一个文件中,source 开始于一个 `@´ 后面跟随文件名。
  • short_src 一个可打印版的 source,用于错误信息。
  • linedefined 函数定义起始的行号。
  • what 如果这是一个Lua函数,显示 "Lua" 字符串, "C" 为C 函数,"main" 如果这是一个语句段的main部分,"tail" 如果这是一个做了尾部调用的函数。在后面的情况里,Lua 没有其他关于这个函部的信息。
  • currentline 代表当前函数执行到的行数。如果没有行信息可用,currentline 被设置为 -1
  • name 一个所给函数合理的函数名。因为函数在Lua中属于第一类值,它们没有固定的名字:一些函数可能是多个全局变量的值,其他的可能只储存在一个表字段里。lua_getinfo 函数检测函数如何被调用或者是否为一个全局变量的值以寻找一个合适的名字。如果找不到合适的名字,name 被设置为 NULL
  • namewhat name 字段的解释。根据函数如何被调用,namewhat 的值可以是 "global", "local", "method", "field" 或者 "" (空字符串)。(当没有其他可选项的时候Lua使用空字符串代替)
  • nups 函数上值的数量。

4.2 - 操作局部变量和上值 Manipulating Local Variables and Upvalues

为了更多的操作局部变量和上值,调试接口使用索引:第一个参数或者局部变量索引为 1,以此类推,直到最后一个活动的局部变量。整个函数中的活动的上值没有特定的顺序。

下面的函数允许操作一个所给激活记录的局部变量:

       const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);

   const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);

参数 ar 必须是一个被前一个 lua_getstack 调用填充的有效的激活记录或者作为一个钩子函数的参数(见 4.3)。lua_getlocal 获得一个局部变量的索引 n,将变量的值压入栈,并且返回变量名。lua_setlocal 从栈顶分配一个值给变量并且返回变量名。当索引超过活动的局部变量的数量时,两个函数都返回 NULL

以下的函部可以操作所给函数的上值(不像局部变量,函数的上值即使在函数不处于活动状态的时候都可以被访问):

       const char *lua_getupvalue (lua_State *L, int funcindex, int n);

   const char *lua_setupvalue (lua_State *L, int funcindex, int n);

这些函数可以作为Lua 函数使用也可以作为C 函数使用。(作为Lua 函数,上值是函数外部使用的局部变量,因此它被包含在函数闭包中。)funcindex 指向栈中的一个函数。lua_getupvalue 得到一个上值的索引 n,将上值的值压入栈,并返回其变量名。lua_setupvalue 从栈顶分配一个值给上值并返回变量名。当索引大于上值数量时,两个函数都返回 NULL。对于C 函数来说,这些函数使用空字符串作为所有上值的变量名。

作为一个例子,下面的函数列举了所给等级的栈中的函数的所有局部变量名和上值变量名:

       int listvars (lua_State *L, int level) {

     lua_Debug ar;

     int i;

     const char *name;

     if (lua_getstack(L, level, &amp;ar) == 0)

       return 0;  /* failure: no such level in the stack */

     i = 1;

     while ((name = lua_getlocal(L, &amp;ar, i++)) != NULL) {

       printf("local %d %s\n", i-1, name);

       lua_pop(L, 1);  /* remove variable value */

     }

     lua_getinfo(L, "f", &amp;ar);  /* retrieves function */

     i = 1;

     while ((name = lua_getupvalue(L, -1, i++)) != NULL) {

       printf("upvalue %d %s\n", i-1, name);

       lua_pop(L, 1);  /* remove upvalue value */

     }

     return 1;

   }

4.3 - 钩子 Hooks

Lua offers a mechanism of hooks, which are user-defined C functions that are called during the program execution. A hook may be called in four different events: a call event, when Lua calls a function; a return event, when Lua returns from a function; a line event, when Lua starts executing a new line of code; and a count event, which happens every "count" instructions. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET (or LUA_HOOKTAILRET, see below), LUA_HOOKLINE, and LUA_HOOKCOUNT.

A hook has type lua_Hook, defined as follows:

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

You can set the hook with the following function:

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

func is the hook. mask specifies on which events the hook will be called: It is formed by a disjunction of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below:

  • The call hook is called when the interpreter calls a function. The hook is called just after Lua enters the new function.
  • The return hook is called when the interpreter returns from a function. The hook is called just before Lua leaves the function.
  • The line hook is called when the interpreter is about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing a Lua function.)
  • The count hook is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.)

A hook is disabled by setting mask to zero.

You can get the current hook, the current mask, and the current count with the following functions:

       lua_Hook lua_gethook      (lua_State *L);

   int      lua_gethookmask  (lua_State *L);

   int      lua_gethookcount (lua_State *L);

Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo. For return events, event may be LUA_HOOKRET, the normal value, or LUA_HOOKTAILRET. In the latter case, Lua is simulating a return from a function that did a tail call; in this case, it is useless to call lua_getinfo.

While Lua is running a hook, it disables other calls to hooks. Therefore, if a hook calls back Lua to execute a function or a chunk, that execution occurs without any calls to hooks.

5 - 标准库

The standard libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to "outside" services (e.g., I/O); and others could be implemented in Lua itself, but are quite useful or have critical performance to deserve an implementation in C (e.g., sort).

All libraries are implemented through the official C API and are provided as separate C modules. Currently, Lua has the following standard libraries:

  • 基本库 basic library;
  • 字符串操作 string manipulation;
  • 表操作 table manipulation;
  • 数学函数 (sin, log 等等)mathematical functions (sin, log, etc.);
  • 输入输出 input and output;
  • 操作系统机制 operating system facilities;
  • 调试机制 debug facilities.

Except for the basic library, each library provides all its functions as fields of a global table or as methods of its objects.

To have access to these libraries, the C host program must first call the functions luaopen_base (for the basic library), luaopen_string (for the string library), luaopen_table (for the table library), luaopen_math (for the mathematical library), luaopen_io (for the I/O and the Operating System libraries), and luaopen_debug (for the debug library). These functions are declared in lualib.h.

5.1 - 基本函数 Basic Functions

The basic library provides some core functions to Lua. If you do not include this library in your application, you should check carefully whether you need to provide some alternative implementation for some of its facilities.

assert (v [, message])

Issues an error when the value of its argument v is nil or false; otherwise, returns this value. message is an error message; when absent, it defaults to "assertion failed!"

collectgarbage ([limit])

Sets the garbage-collection threshold to the given limit (in Kbytes) and checks it against the byte counter. If the new threshold is smaller than the byte counter, then Lua immediately runs the garbage collector (see 2.9). If limit is absent, it defaults to zero (thus forcing a garbage-collection cycle).

dofile (filename)

Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns any value returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, it does not run in protected mode).

error (message [, level])

Terminates the last protected function called and returns message as the error message. Function error never returns.

The level argument specifies where the error message points the error. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on.

_G

A global variable (not a function) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment. (Use setfenv to change environments.)

getfenv (f)

Returns the current environment in use by the function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1.

If the environment has a "__fenv" field, returns the associated value, instead of the environment.

getmetatable (object)

If the object does not have a metatable, returns nil. Otherwise, if the object's metatable has a "__metatable" field, returns the associated value. Otherwise, returns the metatable of the given object.

gcinfo ()

Returns two results: the number of Kbytes of dynamic memory that Lua is using and the current garbage collector threshold (also in Kbytes).

ipairs (t)

Returns an iterator function, the table t, and 0, so that the construction

       for i,v in ipairs(t) do ... end

will iterate over the pairs (1,t[1]), (2,t[2]), ..., up to the first integer key with a nil value in the table.

loadfile (filename)

Loads a file as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.

loadlib (libname, funcname)

Links the program with the dynamic C library libname. Inside this library, looks for a function funcname and returns this function as a C function.

libname must be the complete file name of the C library, including any eventual path and extension.

This function is not supported by ANSI C. As such, it is only available on some platforms (Windows, Linux, Solaris, BSD, plus other Unix systems that support the dlfcn standard).

loadstring (string [, chunkname])

Loads a string as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.

The optional parameter chunkname is the name to be used in error messages and debug information.

To load and run a given string, use the idiom

      assert(loadstring(s))()

next (table [, index])

Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and the value associated with the index. When called with nil as its second argument, next returns the first index of the table and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil.

Lua has no declaration of fields; There is no difference between a field not present in a table or a field with value nil. Therefore, next only considers fields with non-nil values. The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the ipairs function.)

The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table.

pairs (t)

Returns the next function and the table t (plus a nil), so that the construction

       for k,v in pairs(t) do ... end

will iterate over all key-value pairs of table t.

pcall (f, arg1, arg2, ...)

Calls function f with the given arguments in protected mode. That means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.

print (e1, e2, ...)

Receives any number of arguments, and prints their values in stdout, using the tostring function to convert them to strings. This function is not intended for formatted output, but only as a quick way to show a value, typically for debugging. For formatted output, use format (see 5.3).

rawequal (v1, v2)

Checks whether v1 is equal to v2, without invoking any metamethod. Returns a boolean.

rawget (table, index)

Gets the real value of table[index], without invoking any metamethod. table must be a table; index is any value different from nil.

rawset (table, index, value)

Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index is any value different from nil, and value is any Lua value.

require (packagename)

Loads the given package. The function starts by looking into the table _LOADED to determine whether packagename is already loaded. If it is, then require returns the value that the package returned when it was first loaded. Otherwise, it searches a path looking for a file to load.

If the global variable LUA_PATH is a string, this string is the path. Otherwise, require tries the environment variable LUA_PATH. As a last resort, it uses the predefined path "?;?.lua".

The path is a sequence of templates separated by semicolons. For each template, require will change each interrogation mark in the template to packagename, and then will try to load the resulting file name. So, for instance, if the path is

  "./?.lua;./?.lc;/usr/local/?/?.lua;/lasttry"



a require "mod" will try to load the files ./mod.lua, ./mod.lc, /usr/local/mod/mod.lua, and /lasttry, in that order.

The function stops the search as soon as it can load a file, and then it runs the file. After that, it associates, in table _LOADED, the package name with the value that the package returned, and returns that value. If the package returns nil (or no value), require converts this value to true. If the package returns false, require also returns false. However, as the mark in table _LOADED is false, any new attempt to reload the file will happen as if the package was not loaded (that is, the package will be loaded again).

If there is any error loading or running the file, or if it cannot find any file in the path, then require signals an error.

While running a file, require defines the global variable _REQUIREDNAME with the package name. The package being loaded always runs within the global environment.

setfenv (f, table)

Sets the current environment to be used by the given function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling setfenv.

As a special case, when f is 0 setfenv changes the global environment of the running thread.

If the original environment has a "__fenv" field, setfenv raises an error.

setmetatable (table, metatable)

Sets the metatable for the given table. (You cannot change the metatable of a userdata from Lua.) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, raises an error.

tonumber (e [, base])

Tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns that number; otherwise, it returns nil.

An optional argument specifies the base to interpret the numeral. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter `A´ (in either upper or lower case) represents 10, `B´ represents 11, and so forth, with `Z´ representing 35. In base 10 (the default), the number may have a decimal part, as well as an optional exponent part (see 2.2.1). In other bases, only unsigned integers are accepted.

tostring (e)

Receives an argument of any type and converts it to a string in a reasonable format. For complete control of how numbers are converted, use format (see 5.3).

If the metatable of e has a "__tostring" field, tostring calls the corresponding value with e as argument, and uses the result of the call as its result.

type (v)

Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean, "table", "function", "thread", and "userdata".

unpack (list)

Returns all elements from the given list. This function is equivalent to

  return list[1], list[2], ..., list[n]



except that the above code can be written only for a fixed n. The number n is the size of the list, as defined for the table.getn function.

_VERSION

A global variable (not a function) that holds a string containing the current interpreter version. The current content of this string is "Lua 5.0".

xpcall (f, err)

This function is similar to pcall, except that you can set a new error handler.

xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err.

5.2 - Coroutine Manipulation

The operations related to coroutines comprise a sub-library of the basic library and come inside the table coroutine. See 2.10 for a general description of coroutines.

coroutine.create (f)

Creates a new coroutine, with body f. f must be a Lua function. Returns this new coroutine, an object with type "thread".

coroutine.resume (co, val1, ...)

Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The arguments val1, ... go as the arguments to the body function. If the coroutine has yielded, resume restarts it; the arguments val1, ... go as the results from the yield.

If the coroutine runs without any errors, resume returns true plus any values passed to yield (if the coroutine yields) or any values returned by the body function (if the coroutine terminates). If there is any error, resume returns false plus the error message.

coroutine.status (co)

Returns the status of coroutine co, as a string: "running", if the coroutine is running (that is, it called status); "suspended", if the coroutine is suspended in a call to yield, or if it has not started running yet; and "dead" if the coroutine has finished its body function, or if it has stopped with an error.

coroutine.wrap (f)

Creates a new coroutine, with body f. f must be a Lua function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.

coroutine.yield (val1, ...)

Suspends the execution of the calling coroutine. The coroutine cannot be running neither a C function, nor a metamethod, nor an iterator. Any arguments to yield go as extra results to resume.

5.3 - String Manipulation

This library provides generic functions for string manipulation, such as finding and extracting substrings, and pattern matching. When indexing a string in Lua, the first character is at position 1 (not at 0, as in C). Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the string. Thus, the last character is at position -1, and so on.

The string library provides all its functions inside the table string.

string.byte (s [, i])

Returns the internal numerical code of the i-th character of s, or nil if the index is out of range. If i is absent, then it is assumed to be 1. i may be negative.

Note that numerical codes are not necessarily portable across platforms.

string.char (i1, i2, ...)

Receives 0 or more integers. Returns a string with length equal to the number of arguments, in which each character has the internal numerical code equal to its correspondent argument.

Note that numerical codes are not necessarily portable across platforms.

string.dump (function)

Returns a binary representation of the given function, so that a later loadstring on that string returns a copy of the function. function must be a Lua function without upvalues.

string.find (s, pattern [, init [, plain]])

Looks for the first match of pattern in the string s. If it finds one, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil. If the pattern specifies captures (see string.gsub below), the captured strings are returned as extra results. A third, optional numerical argument init specifies where to start the search; it may be negative and its default value is 1. A value of true as a fourth, optional argument plain turns off the pattern matching facilities, so the function does a plain "find substring" operation, with no characters in pattern being considered "magic". Note that if plain is given, then init must be given too.

string.len (s)

Receives a string and returns its length. The empty string "" has length 0. Embedded zeros are counted, so "a\000b\000c" has length 5.

string.lower (s)

Receives a string and returns a copy of that string with all uppercase letters changed to lowercase. All other characters are left unchanged. The definition of what is an uppercase letter depends on the current locale.

string.rep (s, n)

Returns a string that is the concatenation of n copies of the string s.

string.sub (s, i [, j])

Returns the substring of s that starts at i and continues until j; i and j may be negative. If j is absent, then it is assumed to be equal to -1 (which is the same as the string length). In particular, the call string.sub(s,1,j) returns a prefix of s with length j, and string.sub(s, -i) returns a suffix of s with length i.

string.upper (s)

Receives a string and returns a copy of that string with all lowercase letters changed to uppercase. All other characters are left unchanged. The definition of what is a lowercase letter depends on the current locale.

string.format (formatstring, e1, e2, ...)

Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). The format string follows the same rules as the printf family of standard C functions. The only differences are that the options/modifiers *, l, L, n, p, and h are not supported, and there is an extra option, q. The q option formats a string in a form suitable to be safely read back by the Lua interpreter: The string is written between double quotes, and all double quotes, newlines, and backslashes in the string are correctly escaped when written. For instance, the call

       string.format('%q', 'a string with "quotes" and \n new line')

will produce the string:

"a string with \"quotes\" and \

new line”

The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument, whereas q and s expect a string. The * modifier can be simulated by building the appropriate format string. For example, "%*g" can be simulated with "%"..width.."g".

String values to be formatted with %s cannot contain embedded zeros.

string.gfind (s, pat)

Returns an iterator function that, each time it is called, returns the next captures from pattern pat over string s.

If pat specifies no captures, then the whole match is produced in each call.

As an example, the following loop

  s = "hello world from Lua"

for w in string.gfind(s, “%a+”) do

print(w)

end

will iterate over all the words from string s, printing one per line. The next example collects all pairs key=value from the given string into a table:

  t = {}

s = “from=world, to=Lua”

for k, v in string.gfind(s, “(%w+)=(%w+)”) do

t[k] = v

end

string.gsub (s, pat, repl [, n])

Returns a copy of s in which all occurrences of the pattern pat have been replaced by a replacement string specified by repl. gsub also returns, as a second value, the total number of substitutions made.

If repl is a string, then its value is used for replacement. Any sequence in repl of the form %n, with n between 1 and 9, stands for the value of the n-th captured substring (see below).

If repl is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order; if the pattern specifies no captures, then the whole match is passed as a sole argument. If the value returned by this function is a string, then it is used as the replacement string; otherwise, the replacement string is the empty string.

The optional last parameter n limits the maximum number of substitutions to occur. For instance, when n is 1 only the first occurrence of pat is replaced.

Here are some examples:

   x = string.gsub("hello world", "(%w+)", "%1 %1")

–> x=”hello hello world world”

x = string.gsub(“hello world”, “(%w+)”, “%1 %1”, 1)

–> x=”hello hello world”

x = string.gsub(“hello world from Lua”, “(%w+)%s*(%w+)”, “%2 %1”)

–> x=”world hello Lua from”

x = string.gsub(“home = $HOME, user = $USER”, “%$(%w+)”, os.getenv)

–> x=”home = /home/roberto, user = roberto”

x = string.gsub(“4+5 = $return 4+5$”, “%$(.-)%$”, function (s)

     return loadstring(s)()

   end)

–> x=”4+5 = 9”

local t = {name=”lua”, version=”5.0”}

x = string.gsub(“$name_$version.tar.gz”, “%$(%w+)”, function (v)

     return t[v]

   end)

–> x=”lua_5.0.tar.gz”

Patterns

A character class is used to represent a set of characters. The following combinations are allowed in describing a character class:

  • x (where x is not one of the magic characters ^$()%.[]*+-?) --- represents the character x itself.
  • . --- (a dot) represents all characters.
  • %a --- represents all letters.
  • %c --- represents all control characters.
  • %d --- represents all digits.
  • %l --- represents all lowercase letters.
  • %p --- represents all punctuation characters.
  • %s --- represents all space characters.
  • %u --- represents all uppercase letters.
  • %w --- represents all alphanumeric characters.
  • %x --- represents all hexadecimal digits.
  • %z --- represents the character with representation 0.
  • %x (where x is any non-alphanumeric character) --- represents the character x. This is the standard way to escape the magic characters. Any punctuation character (even the non magic) can be preceded by a `%´ when used to represent itself in a pattern.
  • [set] --- represents the class which is the union of all characters in set. A range of characters may be specified by separating the end characters of the range with a `-´. All classes %x described above may also be used as components in set. All other characters in set represent themselves. For example, [%w_] (or [_%w]) represents all alphanumeric characters plus the underscore, [0-7] represents the octal digits, and [0-7%l%-] represents the octal digits plus the lowercase letters plus the `-´ character.

    The interaction between ranges and classes is not defined. Therefore, patterns like [%a-z] or [a-%%] have no meaning.

  • [^set] --- represents the complement of set, where set is interpreted as above.

For all classes represented by single letters (%a, %c, etc.), the corresponding uppercase letter represents the complement of the class. For instance, %S represents all non-space characters.

The definitions of letter, space, and other character groups depend on the current locale. In particular, the class [a-z] may not be equivalent to %l. The second form should be preferred for portability.

A pattern item may be

  • a single character class, which matches any single character in the class;
  • a single character class followed by `*´, which matches 0 or more repetitions of characters in the class. These repetition items will always match the longest possible sequence;
  • a single character class followed by `+´, which matches 1 or more repetitions of characters in the class. These repetition items will always match the longest possible sequence;
  • a single character class followed by `-´, which also matches 0 or more repetitions of characters in the class. Unlike `*´, these repetition items will always match the shortest possible sequence;
  • a single character class followed by `?´, which matches 0 or 1 occurrence of a character in the class;
  • %n, for n between 1 and 9; such item matches a substring equal to the n-th captured string (see below);
  • %bxy, where x and y are two distinct characters; such item matches strings that start with x, end with y, and where the x and y are balanced. This means that, if one reads the string from left to right, counting +1 for an x and -1 for a y, the ending y is the first y where the count reaches 0. For instance, the item %b() matches expressions with balanced parentheses.

A pattern is a sequence of pattern items. A `^´ at the beginning of a pattern anchors the match at the beginning of the subject string. A `$´ at the end of a pattern anchors the match at the end of the subject string. At other positions, `^´ and `$´ have no special meaning and represent themselves.

A pattern may contain sub-patterns enclosed in parentheses; they describe captures. When a match succeeds, the substrings of the subject string that match captures are stored (captured) for future use. Captures are numbered according to their left parentheses. For instance, in the pattern "(a*(.)%w(%s*))", the part of the string matching "a*(.)%w(%s*)" is stored as the first capture (and therefore has number 1); the character matching . is captured with number 2, and the part matching %s* has number 3.

As a special case, the empty capture () captures the current string position (a number). For instance, if we apply the pattern "()aa()" on the string "flaaap", there will be two captures: 3 and 5.

A pattern cannot contain embedded zeros. Use %z instead.

5.4 - Table Manipulation

This library provides generic functions for table manipulation. It provides all its functions inside the table table.

Most functions in the table library assume that the table represents an array or a list. For those functions, an important concept is the size of the array. There are three ways to specify that size:

  • the field "n" --- When the table has a field "n" with a numerical value, that value is assumed as its size.
  • setn --- You can call the table.setn function to explicitly set the size of a table.
  • implicit size --- Otherwise, the size of the object is one less the first integer index with a nil value.

For more details, see the descriptions of the table.getn and table.setn functions.

table.concat (table [, sep [, i [, j]]])

Returns table[i]..sep..table[i+1] ... sep..table[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is the size of the table. If i is greater than j, returns the empty string.

table.foreach (table, f)

Executes the given f over all elements of table. For each element, f is called with the index and respective value as arguments. If f returns a non-nil value, then the loop is broken, and this value is returned as the final value of foreach.

See the next function for extra information about table traversals.

table.foreachi (table, f)

Executes the given f over the numerical indices of table. For each index, f is called with the index and respective value as arguments. Indices are visited in sequential order, from 1 to n, where n is the size of the table (see 5.4). If f returns a non-nil value, then the loop is broken and this value is returned as the result of foreachi.

table.getn (table)

Returns the size of a table, when seen as a list. If the table has an n field with a numeric value, this value is the size of the table. Otherwise, if there was a previous call to table.setn over this table, the respective value is returned. Otherwise, the size is one less the first integer index with a nil value.

table.sort (table [, comp])

Sorts table elements in a given order, in-place, from table[1] to table[n], where n is the size of the table (see 5.4). If comp is given, then it must be a function that receives two table elements, and returns true when the first is less than the second (so that not comp(a[i+1],a[i]) will be true after the sort). If comp is not given, then the standard Lua operator < is used instead.

The sort algorithm is not stable, that is, elements considered equal by the given order may have their relative positions changed by the sort.

table.insert (table, [pos,] value)

Inserts element value at position pos in table, shifting up other elements to open space, if necessary. The default value for pos is n+1, where n is the size of the table (see 5.4), so that a call table.insert(t,x) inserts x at the end of table t. This function also updates the size of the table by calling table.setn(table, n+1).

table.remove (table [, pos])

Removes from table the element at position pos, shifting down other elements to close the space, if necessary. Returns the value of the removed element. The default value for pos is n, where n is the size of the table (see 5.4), so that a call table.remove(t) removes the last element of table t. This function also updates the size of the table by calling table.setn(table, n-1).

table.setn (table, n)

Updates the size of a table. If the table has a field "n" with a numerical value, that value is changed to the given n. Otherwise, it updates an internal state so that subsequent calls to table.getn(table) return n.

5.5 - Mathematical Functions

This library is an interface to most of the functions of the standard C math library. (Some have slightly different names.) It provides all its functions inside the table math. In addition, it registers the global __pow for the binary exponentiation operator ^, so that x^y returns xy. The library provides the following functions:

       math.abs     math.acos    math.asin    math.atan    math.atan2

   math.ceil    math.cos     math.deg     math.exp     math.floor

   math.log     math.log10   math.max     math.min     math.mod

   math.pow     math.rad     math.sin     math.sqrt    math.tan

   math.frexp   math.ldexp   math.random  math.randomseed

plus a variable math.pi. Most of them are only interfaces to the corresponding functions in the C library. All trigonometric functions work in radians (previous versions of Lua used degrees). The functions math.deg and math.rad convert between radians and degrees.

The function math.max returns the maximum value of its numeric arguments. Similarly, math.min computes the minimum. Both can be used with 1, 2, or more arguments.

The functions math.random and math.randomseed are interfaces to the simple random generator functions rand and srand that are provided by ANSI C. (No guarantees can be given for their statistical properties.) When called without arguments, math.random returns a pseudo-random real number in the range [0,1). When called with a number n, math.random returns a pseudo-random integer in the range [1,n]. When called with two arguments, l and u, math.random returns a pseudo-random integer in the range [l,u]. The math.randomseed function sets a "seed" for the pseudo-random generator: Equal seeds produce equal sequences of numbers.

5.6 - Input and Output Facilities

The I/O library provides two different styles for file manipulation. The first one uses implicit file descriptors, that is, there are operations to set a default input file and a default output file, and all input/output operations are over those default files. The second style uses explicit file descriptors.

When using implicit file descriptors, all operations are supplied by table io. When using explicit file descriptors, the operation io.open returns a file descriptor and then all operations are supplied as methods by the file descriptor.

The table io also provides three predefined file descriptors with their usual meanings from C: io.stdin, io.stdout, and io.stderr.

A file handle is a userdata containing the file stream (FILE*), with a distinctive metatable created by the I/O library.

Unless otherwise stated, all I/O functions return nil on failure (plus an error message as a second result) and some value different from nil on success.

io.close ([file])

Equivalent to file:close. Without a file, closes the default output file.

io.flush ()

Equivalent to file:flush over the default output file.

io.input ([file])

When called with a file name, it opens the named file (in text mode), and sets its handle as the default input file. When called with a file handle, it simply sets that file handle as the default input file. When called without parameters, it returns the current default input file.

In case of errors this function raises the error, instead of returning an error code.

io.lines ([filename])

Opens the given file name in read mode and returns an iterator function that, each time it is called, returns a new line from the file. Therefore, the construction

       for line in io.lines(filename) do ... end

will iterate over all lines of the file. When the iterator function detects the end of file, it returns nil (to finish the loop) and automatically closes the file.

The call io.lines() (without a file name) is equivalent to io.input():lines(), that is, it iterates over the lines of the default input file.

io.open (filename [, mode])

This function opens a file, in the mode specified in the string mode. It returns a new file handle, or, in case of errors, nil plus an error message.

The mode string can be any of the following:

  • "r" read mode (the default);
  • "w" write mode;
  • "a" append mode;
  • "r+" update mode, all previous data is preserved;
  • "w+" update mode, all previous data is erased;
  • "a+" append update mode, previous data is preserved, writing is only allowed at the end of file.

The mode string may also have a b at the end, which is needed in some systems to open the file in binary mode. This string is exactly what is used in the standard C function fopen.

io.output ([file])

Similar to io.input, but operates over the default output file.

io.read (format1, ...)

Equivalent to io.input():read.

io.tmpfile ()

Returns a handle for a temporary file. This file is open in update mode and it is automatically removed when the program ends.

io.type (obj)

Checks whether obj is a valid file handle. Returns the string "file" if obj is an open file handle, "closed file" if obj is a closed file handle, and nil if obj is not a file handle.

io.write (value1, ...)

Equivalent to io.output():write.

file:close ()

Closes file.

file:flush ()

Saves any written data to file.

file:lines ()

Returns an iterator function that, each time it is called, returns a new line from the file. Therefore, the construction

       for line in file:lines() do ... end

will iterate over all lines of the file. (Unlike io.lines, this function does not close the file when the loop ends.)

file:read (format1, ...)

Reads the file file, according to the given formats, which specify what to read. For each format, the function returns a string (or a number) with the characters read, or nil if it cannot read data with the specified format. When called without formats, it uses a default format that reads the entire next line (see below).

The available formats are

  • "*n" reads a number; this is the only format that returns a number instead of a string.
  • "*a" reads the whole file, starting at the current position. On end of file, it returns the empty string.
  • "*l" reads the next line (skipping the end of line), returning nil on end of file. This is the default format.
  • number reads a string with up to that number of characters, returning nil on end of file. If number is zero, it reads nothing and returns an empty string, or nil on end of file.

file:seek ([whence] [, offset])

Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence, as follows:

  • "set" base is position 0 (beginning of the file);
  • "cur" base is current position;
  • "end" base is end of file;

In case of success, function seek returns the final file position, measured in bytes from the beginning of the file. If this function fails, it returns nil, plus a string describing the error.

The default value for whence is "cur", and for offset is 0. Therefore, the call file:seek() returns the current file position, without changing it; the call file:seek("set") sets the position to the beginning of the file (and returns 0); and the call file:seek("end") sets the position to the end of the file, and returns its size.

file:write (value1, ...)

Writes the value of each of its arguments to the filehandle file. The arguments must be strings or numbers. To write other values, use tostring or string.format before write.

5.7 - Operating System Facilities

This library is implemented through table os.

os.clock ()

Returns an approximation of the amount of CPU time used by the program, in seconds.

os.date ([format [, time]])

Returns a string or a table containing date and time, formatted according to the given string format.

If the time argument is present, this is the time to be formatted (see the os.time function for a description of this value). Otherwise, date formats the current time.

If format starts with `!´, then the date is formatted in Coordinated Universal Time. After that optional character, if format is *t, then date returns a table with the following fields: year (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), and isdst (daylight saving flag, a boolean).

If format is not *t, then date returns the date as a string, formatted according to the same rules as the C function strftime.

When called without arguments, date returns a reasonable date and time representation that depends on the host system and on the current locale (that is, os.date() is equivalent to os.date("%c")).

os.difftime (t2, t1)

Returns the number of seconds from time t1 to time t2. In Posix, Windows, and some other systems, this value is exactly t2-t1.

os.execute (command)

This function is equivalent to the C function system. It passes command to be executed by an operating system shell. It returns a status code, which is system-dependent.

os.exit ([code])

Calls the C function exit, with an optional code, to terminate the host program. The default value for code is the success code.

os.getenv (varname)

Returns the value of the process environment variable varname, or nil if the variable is not defined.

os.remove (filename)

Deletes the file with the given name. If this function fails, it returns nil, plus a string describing the error.

os.rename (oldname, newname)

Renames file named oldname to newname. If this function fails, it returns nil, plus a string describing the error.

os.setlocale (locale [, category])

Sets the current locale of the program. locale is a string specifying a locale; category is an optional string describing which category to change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category is "all". The function returns the name of the new locale, or nil if the request cannot be honored.

os.time ([table])

Returns the current time when called without arguments, or a time representing the date and time specified by the given table. This table must have fields year, month, and day, and may have fields hour, min, sec, and isdst (for a description of these fields, see the os.date function).

The returned value is a number, whose meaning depends on your system. In Posix, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch"). In other systems, the meaning is not specified, and the number returned by time can be used only as an argument to date and difftime.

os.tmpname ()

Returns a string with a file name that can be used for a temporary file. The file must be explicitly opened before its use and removed when no longer needed.

This function is equivalent to the tmpnam C function, and many people (and even some compilers!) advise against its use, because between the time you call this function and the time you open the file, it is possible for another process to create a file with the same name.

5.8 - The Reflexive Debug Interface

The debug library provides the functionality of the debug interface to Lua programs. You should exert care when using this library. The functions provided here should be used exclusively for debugging and similar tasks, such as profiling. Please resist the temptation to use them as a usual programming tool: They can be very slow. Moreover, setlocal and getlocal violate the privacy of local variables and therefore can compromise some otherwise secure code.

All functions in this library are provided inside a debug table.

debug.debug ()

Enters an interactive mode with the user, running each string that the user enters. Using simple commands and other debug facilities, the user can inspect global and local variables, change their values, evaluate expressions, and so on. A line containing only the word cont finishes this function, so that the caller continues its execution.

Note that commands for debug.debug are not lexically nested with any function, so they have no direct access to local variables.

debug.gethook ()

Returns the current hook settings, as three values: the current hook function, the current hook mask, and the current hook count (as set by the debug.sethook function).

debug.getinfo (function [, what])

This function returns a table with information about a function. You can give the function directly, or you can give a number as the value of function, which means the function running at level function of the call stack: Level 0 is the current function (getinfo itself); level 1 is the function that called getinfo; and so on. If function is a number larger than the number of active functions, then getinfo returns nil.

The returned table contains all the fields returned by lua_getinfo, with the string what describing which fields to fill in. The default for what is to get all information available. If present, the option `f´ adds a field named func with the function itself.

For instance, the expression debug.getinfo(1,"n").name returns the name of the current function, if a reasonable name can be found, and debug.getinfo(print) returns a table with all available information about the print function.

debug.getlocal (level, local)

This function returns the name and the value of the local variable with index local of the function at level level of the stack. (The first parameter or local variable has index 1, and so on, until the last active local variable.) The function returns nil if there is no local variable with the given index, and raises an error when called with a level out of range. (You can call debug.getinfo to check whether the level is valid.)

debug.getupvalue (func, up)

This function returns the name and the value of the upvalue with index up of the function func. The function returns nil if there is no upvalue with the given index.

debug.setlocal (level, local, value)

This function assigns the value value to the local variable with index local of the function at level level of the stack. The function returns nil if there is no local variable with the given index, and raises an error when called with a level out of range. (You can call getinfo to check whether the level is valid.)

debug.setupvalue (func, up, value)

This function assigns the value value to the upvalue with index up of the function func. The function returns nil if there is no upvalue with the given index.

debug.sethook (hook, mask [, count])

Sets the given function as a hook. The string mask and the number count describe when the hook will be called. The string mask may have the following characters, with the given meaning:

  • "c" The hook is called every time Lua calls a function;
  • "r" The hook is called every time Lua returns from a function;
  • "l" The hook is called every time Lua enters a new line of code.

With a count different from zero, the hook is called after every count instructions.

When called without arguments, the debug.sethook function turns off the hook.

When the hook is called, its first parameter is always a string describing the event that triggered its call: "call", "return" (or "tail return"), "line", and "count". Moreover, for line events, it also gets as its second parameter the new line number. Inside a hook, you can call getinfo with level 2 to get more information about the running function (level 0 is the getinfo function, and level 1 is the hook function), unless the event is "tail return". In this case, Lua is only simulating the return, and a call to getinfo will return invalid data.

debug.traceback ([message])

Returns a string with a traceback of the call stack. An optional message string is appended at the beginning of the traceback. This function is typically used with xpcall to produce better error messages.

6 - Lua 独立程序 Lua Stand-alone

尽管Lua被设计为一种内嵌于C 语言宿主程序中的扩展语言,它还是经常被用作一个独立程序语言。一个Lua的解释程序将Lua作为一个独立的语言,我们称之为简化的 lua,它提供了标准的发行版本。独立的解释器包含了所有标准库加上反射性的调试接口。它的使用方式如下:

      lua [options] [script [args]]

options 可以是以下内容:

  • - 标准输入(stdin) 当作文件执行;
  • -e stat 执行字符串 stat
  • -l file “需要”file 文件;
  • -i 在运行脚本后进入交互模式;
  • -v 打印版本信息;
  • -- 停止处理选项。

在停止处理选项后,lua 运行所给的脚本,传递所给参数 args。当无参数调用时,lua 就像 stdin 是程序的终结时 lua -v -i 所表现的一样,而且还与 lua- 一样。

Before running any argument, the interpreter checks for an environment variable LUA_INIT. If its format is @filename, then lua executes the file. Otherwise, lua executes the string itself.

All options are handled in order, except -i. For instance, an invocation like

       $ lua -e'a=1' -e 'print(a)' script.lua

will first set a to 1, then print a, and finally run the file script.lua. (Here, $ is the shell prompt. Your prompt may be different.)

Before starting to run the script, lua collects all arguments in the command line in a global table called arg. The script name is stored in index 0, the first argument after the script name goes to index 1, and so on. The field n gets the number of arguments after the script name. Any arguments before the script name (that is, the interpreter name plus the options) go to negative indices. For instance, in the call

       $ lua -la.lua b.lua t1 t2

the interpreter first runs the file a.lua, then creates a table

       arg = { [-2] = "lua", [-1] = "-la.lua", [0] = "b.lua",

           [1] = "t1", [2] = "t2"; n = 2 }

and finally runs the file b.lua.

在交互模式中,如果你写入了一个不完整的语句,解释器将等待你的完成。

If the global variable _PROMPT is defined as a string, then its value is used as the prompt. Therefore, the prompt can be changed directly on the command line:

       $ lua -e"_PROMPT='myprompt> '" -i



(the outer pair of quotes is for the shell, the inner is for Lua), or in any Lua programs by assigning to _PROMPT. Note the use of -i to enter interactive mode; otherwise, the program would end just after the assignment to _PROMPT.

在Unix系统中,Lua脚本可以用 chmod +x 将其变成可执行程序,并且通过 #! 形式,例如

#!/usr/local/bin/lua



(当然,Lua解释器的位置可能有所不同,如果 lua 在你的 PATH 中,那么

#!/usr/bin/env lua

就是一个更通用的解决方案。)

致谢

The Lua team is grateful to Tecgraf for its continued support to Lua. We thank everyone at Tecgraf, specially the head of the group, Marcelo Gattass. At the risk of omitting several names, we also thank the following individuals for supporting, contributing to, and spreading the word about Lua: Alan Watson. André Clinio, André Costa, Antonio Scuri, Asko Kauppi, Bret Mogilefsky, Cameron Laird, Carlos Cassino, Carlos Henrique Levy, Claudio Terra, David Jeske, Ed Ferguson, Edgar Toernig, Erik Hougaard, Jim Mathies, John Belmonte, John Passaniti, John Roll, Jon Erickson, Jon Kleiser, Mark Ian Barlow, Nick Trout, Noemi Rodriguez, Norman Ramsey, Philippe Lhoste, Renata Ratton, Renato Borges, Renato Cerqueira, Reuben Thomas, Stephan Herrmann, Steve Dekorte, Thatcher Ulrich, Tomás Gorham, Vincent Penquerc'h. Thank you!


与以前版本的不兼容性 Incompatibilities with Previous Versions

Lua 5.0 是一个主版本,所有与 Lua 4.0 有一些地方不兼容。

与 v4.0 的不兼容性 Incompatibilities with version 4.0

语言上的变动

  • 整个标签方法模式被元表所替代。The whole tag-method scheme was replaced by metatables.
  • Function calls written between parentheses result in exactly one value.
  • A function call as the last expression in a list constructor (like {a,b,f()}) has all its return values inserted in the list.
  • The precedence of or is smaller than the precedence of and.
  • in, false, and true are reserved words.
  • The old construction for k,v in t, where t is a table, is deprecated (although it is still supported). Use for k,v in pairs(t) instead.
  • When a literal string of the form [[...]] starts with a newline, this newline is ignored.
  • Upvalues in the form %var are obsolete; use external local variables instead.

库的变更

  • Most library functions now are defined inside tables. There is a compatibility script (compat.lua) that redefines most of them as global names.
  • In the math library, angles are expressed in radians. With the compatibility script (compat.lua), functions still work in degrees.
  • The call function is deprecated. Use f(unpack(tab)) instead of call(f, tab) for unprotected calls, or the new pcall function for protected calls.
  • dofile does not handle errors, but simply propagates them.
  • dostring is deprecated. Use loadstring instead.
  • The read option *w is obsolete.
  • The format option %n$ is obsolete.

API 上的改动

  • lua_open 不再需要堆栈大小作为参数(堆栈是动态的)。
  • lua_pushuserdata 已经被废除了。使用 lua_newuserdatalua_pushlightuserdata 来代替它。

Lua 完整语法参考

chunk ::= {stat [`;´]} block ::= chunk stat ::= varlist1 `=´ explist1 | functioncall | do block end | while exp do block end | repeat block until exp | if exp then block {elseif exp then block} [else block] end | return [explist1] | break | for Name `=´ exp `,´ exp [`,´ exp] do block end | for Name {`,´ Name} in explist1 do block end | function funcname funcbody | localfunction Name funcbody | local namelist [init] funcname ::= Name {`.´ Name} [`:´ Name] varlist1 ::= var {`,´ var} var ::= Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name namelist ::= Name {`,´ Name} init ::= `=´ explist1 explist1 ::= {exp `,´} exp exp ::= nil | false | true | Number | Literal | function | prefixexp | tableconstructor | exp binop exp | unop exp prefixexp ::= var | functioncall | `(´ exp `)´ functioncall ::= prefixexp args | prefixexp `:´ Name args args ::= `(´ [explist1] `)´ | tableconstructor | Literal function ::= function funcbody funcbody ::= `(´ [parlist1] `)´ block end parlist1 ::= Name {`,´ Name} [`,´ `...´] | `...´ tableconstructor ::= `{´ [fieldlist] `}´ fieldlist ::= field {fieldsep field} [fieldsep] field ::= `[´ exp `]´ `=´ exp | name `=´ exp | exp fieldsep ::= `,´ | `;´ binop ::= `+´ | `-´ | `*´ | `/´ | `^´ | `..´ | `<´ | `<=´ | `>´ | `>=´ | `==´ | `~=´ | and | or unop ::= `-´ | not

去年我作了一个Lua脚本的C++包装,有许多朋友感兴趣,并尝试使用,我感到受宠若惊。事实上,我作的包装,学习的目的比较强,它还是有许多缺陷的。为了让朋友们少走弯路,我推荐使用LuaPlus作为C++的包装。

LuaPlus是Lua的C++增强,也就是说,LuaPlus本身就是在Lua的源码上进行增强得来的。用它与C++进行合作,是比较好的一个选择。
LuaPlus目前版本为:LuaPlus for Lua 5.01 Distribution Build 1080 (February 28, 2004)。大家可以到http://luaplus.org/ 站点下载:
源码   (http://wwhiz.com/LuaPlus/LuaPlus50_Build1081.zip)
目标码 (http://wwhiz.com/LuaPlus/LuaPlus50_Build1081_Win32Binaries.zip)

我将在下面说明,如何使用LuaPlus,以及如何更方便的让LuaPlus与C++的类合作无间。

1. 调用Lua脚本

    // 创建Lua解释器:
    LuaStateOwner state;
   
    // 执行Lua脚本:
    state->DoString("print('Hello World\n')");
    // 载入Lua脚本文件并执行:
    state->DoFile("C:\\test.lua");
    // 载入编译后的Lua脚本文件并执行:
    state->DoFile("C:\\test.luac");

2. 与Lua脚本互相调用

    // 为Lua脚本设置变量
    state->GetGlobals().SetNumber("myvalue", 123456);
    // 获得Lua变量的值
    int myvalue = state->GetGlobal("myvalue").GetInteger();
   
    // 调用Lua函数
    LuaFunction<int> luaPrint = state->GetGlobal("print");
    luaPrint("Hello World\n");
   
    // 让Lua调用C语言函数
    int add(int a, int b){ return a+b;}
    state->GetGlobals().RegisterDirect("add", add);
    state->DoString("print(add(3,4))");
   
    // 让Lua调用C++类成员函数
    class Test{public: int add(int a, int b){return a+b;}};
    Test test;
    state->GetGlobals().RegisterDirect("add", test, add);
    state->DoString("print(add(3,4))");
   
3. 在Lua脚本中使用C++类
   
    这个稍微有点小麻烦。不过,我包装了一个LuaPlusHelper.h的文件,它可以很轻松的完成这个工作。它的实现也很简单,大家可以从源码上来获得如何用纯LuaPlus实现同样的功能。
    不过,这里仍然有一个限制没有解决:不能使用虚成员函数。不过考虑到我们仅是在Lua调用一下C++函数,并不是要将C++完美的导入到Lua,这个限制完全可以接受。
    另外,类成员变量不能直接在Lua中访问,可以通过类成员函数来访问(比如SetValue/GetValue之类)。

 // 下面是一个简单的C++类:   
 class Logger
 {
 public:
  void LOGMEMBER(const char* message)
  {
   printf("In member function: %s\n", message);
  }
 
  Logger()
  {
   printf("Constructing(%p)...\n", this);
   v = 10;
  }
  virtual ~Logger()
  {
   printf("Destructing(%p)...\n", this);
  }
 
  Logger(int n)
  {
   printf(" -- Constructing[%d](%p)...\n", n, this);
  }
  Logger(Logger* logger)
  {
   printf(" -- Constructing[%p](%p)...\n", logger, this);
   logger->LOGMEMBER(" Call From Constructor\n");
  }
  int SetValue(int val)
  {
   v = val;
  }
  int GetValue()
  {
   return v;
  }
 public:
  int v;
 };

    // 导入到Lua脚本:
    LuaClass<Logger>(state)
 .create("Logger") // 定义构造函数 Logger::Logger()
 .create<int>("Logger2")  // 定义构造函数 Logger::Logger(int)
 .create<Logger*>("Logger3") // 定义构造函数 Logger::Logger(Logger*)
 .destroy("Free")  // 定义析构函数 Logger::~Logger()
 .destroy("__gc")  // 定义析构函数 Logger::~Logger()
 .def("lm", &Logger::LOGMEMBER)  // 定义成员函数 Logger::LOGMEMBER(const char*)
 .def("SetValue", &Logger::SetValue)
 .def("GetValue", &Logger::GetValue);
 
    // 在Lua中使用Logger类(1):
    state->DoString(
        "l = Logger();"  // 调用构造函数 Logger::Logger()
        "l.lm('Hello World 1');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "l.Free();"  // 调用析构函数 Logger::~Logger()
        );

    // 在Lua中使用Logger类(2):
    state->DoString(
        "m = Logger(10);" // 调用构造函数 Logger::Logger(int)
        "m.lm('Hello World 2');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "n = Logger(m);" // 调用构造函数 Logger::Logger(Logger*)
        "n.lm('Hello World 3');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "m.SetValue(11);"
        "print(m.GetValue());"
        "m,n = nil, nil;" // m,n 将由Lua的垃极回收来调用析构函数
        );

4. 将一组C函数归类到Lua模块

    //同上面一样,我采用LuaPlusHelper.h来简化:
    LuaModule(state, "mymodule")
 .def("add", add)
 .def("add2", test, add);
 
    state->DoString(
        "print(mymodule.add(3,4));"
        "print(mymodule.add2(3,4));"
        );

5. 使用Lua的Table数据类型
    // 在Lua中创建Table
    LuaObject table = state->GetGlobals().CreateTable("mytable");
    table.SetInteger("m", 10);
    table.SetNumber("f", 1.99);
    table.SetString("s", "Hello World");
    table.SetWString("ch", L"你好");
    table.SetString(1, "What");
   
    // 相当于Lua中的:
    // mytable = {m=10, f=1.99, s="Hello World", ch=L"你好", "What"}
   
    // 也可以使用table作为key和value:
    state->GetGlobals().CreateTable("nexttable")
        .SetString(table, "Hello")
        .SetObject("obj", table);
    // 相当于Lua中的:
    // nexttable = {mytable="Hello", obj=mytable}
   
    //获得Table的内容:
    LuaObject t2 = state->GetGlobals("mytable");
    int m = t2.GetByName("m").GetInteger();
   
    LuaObject t3 = state->GetGlobals("nexttable");
    std::string str = t3.GetByObject(t2).GetString();
   
6  遍历Table

 LuaStateOwner state;
 state.DoString( "MyTable = { Hi = 5, Hello = 10, Yo = 6 }" );
 
 LuaObject obj = state.GetGlobals()[ "MyTable" ];
 for ( LuaTableIterator it( obj ); it; it.Next() )
 {
     const char* key = it.GetKey().GetString();
     int num = it.GetValue().GetInteger();
 }

篇尾

上面我只是简单的举一些例子来说明LuaPlus以及LuaPlusHelper的使用方法,具体文档请参见LuaPlus。

需要下载LuaPlusHelper,请点这里:
http://www.d2-life.com/LBS/attachments/month_200509/06_zwo3LuaPlusHelper.zip

虎鹤双形拳是南海平洲人林世荣根据洪拳和佛拳改编而成一套拳术。著有《虎鹤双形》一书。他说这套拳能“以小击大,以弱击强,千斤之力得以半两消之”; 又说,能“以横克直,以弱借强,虎爪则如猛虫扑兽,鹤翅则为凌空击水,浩浩如五爪金龙,盘盘如老僧入定,极神化之妙”。自成一格,有“洪头佛尾”之称。流行于南海、番禺、顺德、肇庆、广宁、怀集等地,流行于港澳和南洋一带。它的整套动作108点,模仿虎与鹤两种形象编成。结构有慢有快,有高有低,从慢到快,快慢结合。其内容以洪家桥马、佛家快打、洪家防卫、佛家攻势兼而有之。手形是拳、掌、指、爪、钩。手法有抛、钉(钑)、挂、撞、插。步法有弓步、马步、虚步、跪步、独立步和麒麟步。身形以平稳中正为主,收腹探身为助。拳势威武雄壮,气势磅礡。要求出拳稳定有力,步法落地生根,眼睛灵活有神,身段挺拔端正。





可惜没学玩有空继续。

为什么要用Lua作脚本?
  使用Lua作脚本,主要是因为它小巧玲珑(体积小,运行快),而且它的语法又比较简单明了。不过,使用LuaAPI将Lua引擎集成到程序中,确实有一些不方便——用落木随风网友的话来说,就是"就象用汇编"。当然,现在你不用再这么辛苦了,因为你可以使用LuaWrapper For C++。使用这个工具,在C++中集成Lua脚本就是轻而易举的事。你原有的C++函数和类,几乎不需要任何改变,就可以与Lua脚本共享。
  我们接下来,用实例来说明,如何用LuaWrapper来集成Lua脚本到你的程序中去。

1.  创建Lua引擎
  LuaWrap lua; 或者 LuaWrap* lua = new LuaWrap;
  创建一个LuaWrap对象,就是创建一个Lua脚本引擎。并且根据Lua的特性,你可以创建任意多个Lua引擎,甚至可以分布在不同的线程当中。

2.  装载并执行脚本程序
  你可以从缓冲区中装载Lua脚本:
  lua.LoadString(
    "print('Hello World')"
  );
  当然,你也可以从文件中装入,并执行Lua脚本:
  Lua.LoadFile("./test.lua");
  Lua的脚本,可以是源代码,也可以经过编译后的中间代码。也许你对编译后的中间代码更感兴趣——如果你不希望让源代码赤裸裸的袒露在大家的眼前。

3.  获取和设置Lua变量
  能够获取和设置脚本变量的内容,是一个最基本的功能。你可以使用GetGlobal和SetGlobal函数来做到这一点:
  (1)  获取变量:
    int a = lua.GetGlobal<int>("a");
    LuaTable table = lua.GetGlobal<LuaTable>("t");
    这里,<> 里头的类型,就是想要的变量的类型。
  (2)  设置变量:
    lua.SetGlobal("a", a);
    lua.SetGlobal("t", table);

4.  调用Lua函数
  使用Call函数,就可以很简单的从你的程序中调用Lua函数:
  lua.Call<void>("print", "Hello World");
  int sum = lua.Call<int>("add", 2, 3);
  这里,<> 里头的类型是返回值的类型。

5.  如何让Lua也能调用C++的函数
  精采的地方来了。假如有下面这样的一个函数:
  int add(int a, int b)
  {
    return a + b;
  }
  如果想让它能够让Lua使用,只需将它注册到Lua引擎当中就可以了:
  lua.RegisterFunc("add", int(int,int), add);
  这样,Lua中就可以用直接使用了:
  (Lua脚本)sum = add(1, 3)

  (*) RegisterFunc的功能,就是让你把C++的函数注册到Lua中,供Lua脚本使用。
    第一个参数,是想要在Lua中用的函数名。
    第二个参数,是C++中函数的原型; C++允许函数重载的,你可以使用函数原型,来选择需要注册到Lua引擎中的那个函数。
    第三个参数,就是C++中函数的指针了。

6.  如何能让C++的类在Lua中使用
  我们先看看下面这个C++类:
class MyArray
{
  std::vector<double> array;
public:
  void setvalue(int index, double value);
  double getvalue(int index);
  int size();
  const char* ToString();
};

  你准备要让Lua能够自由访问并操作这个类。很简单,你只需增加几个宏定义就可以了:

class MyArray
{
  std::vector<double> array;
public:
  void setvalue(int index, double value);
  double getvalue(int index);
  int size();
  const char* ToString();
  // 将一个 class 作为一个 Lua 对象是很容易的,只需要增加以下宏定义。
  DEFINE_TYPENAME("My.array");
  BEGIN_REGLUALIB("array")
      LUALIB_ITEM_create("new", MyArray )  // 创建MyArray (注:由于发表的原因,create应为全部大写)
      LUALIB_ITEM_DESTROY("del", MyArray )  // 消除MyArray。
  END_REGLUALIB()
  BEGIN_REGLUALIB_MEMBER()
    LUALIB_ITEM_FUNC("size", int (MyArray*), &MyArray::size)
    LUALIB_ITEM_FUNC("__getindex", double(MyArray*, int), &MyArray::getvalue)  
    LUALIB_ITEM_FUNC("__newindex", void (MyArray*, int, double), &MyArray::setvalue)
    LUALIB_ITEM_FUNC("__tostring", const char* (MyArray*), &MyArray::ToString)
    LUALIB_ITEM_DESTROY("__gc", MyArray )   // 垃圾收集时消除对象用。
  END_REGLUALIB_MEMBER()
};

  只要有了这些宏定义,这个类就是可以在Lua中使用的类了,我们就可以在Lua中注册这个类了:
  lua.Register<MyArray>()

  这样注册以后,我们在Lua中就可以使用这个类了:
  a = array.new()  -- 创建对象,相当于 a = new Myarray
  a[1] = 10  -- 调用__newindex,也就是C++中的 a->setvalue(1, 10)
  a[2] = 20  -- 调用__newindex,也就是C++中的 a->setvalue(2, 20)
  print(
    a,  -- 调用 __tostring,也就是C++中的 a->ToString()
    a:size(), -- 相当于C++中的 a->size()
    a[1], -- 调用__getindex,也就是C++中的a->getvalue(1)
    a[2]) --调用__getindex,也就是C++中的a->getvalue(2)
  array.del(a)  -- 清除对象,相当于 delete a
  a = nil  -- 清空 a,很象C++中的 a = NULL

  当然,你也可以不用del这个对象,而是等待Lua帮你自动进行垃圾回收。在Lua进行垃圾回收时,它会自动调用这个对象的 __gc ,相当于 delete。

  那么,在C++中要创建MyArray对象,并且传递给Lua全局变量怎么办?就象前面讲过的一样,使用SetGlobal:
  MyArray* a = new MyArray;
  lua.SetGlobal("a", a);
  要获取该对象,同样的,应该使用GetGlobal:
  MyArray* a = lua.GetGlobal<MyArray>("a");
  
  对于传递给Lua的对象,就让Lua来管理该对象的生存周期好了。如果你非要删除它的话,你可以使用DelGlobalObject:
  lua.DelGlobalObject<MyArray>("a");
  不过这么做的话,你应当明白你在做什么,因为在Lua的脚本中,可能已经在多处引用了这个对象了。删除了其中一个,将导致其它引用对象失效,从而可能引致系统崩溃。

  (1)  DEFINE_TYPENAME("My.array");
    定义类型的名称。在Lua中,这个类型名称是唯一用来识别C++类型的,你必须为不同的对象给予不同的名称。

  (2)  BEGIN_REGLUALIB("array") … END_REGLUALIB()
    你可以为一个对象定义一个程序库,"array"就是程序库的名字。在程序库中定义的函数是全局函数,在Lua中,使用该函数,需要在函数前加上库的名字,如:array.new()。通常,程序库会包含创建对象的方法。如:
    LUALIB_ITEM_create("new", MyArray )  // 创建MyArray (注:由于发表的原因,create应为全部大写)
    这样子,你才能在Lua中创建MyArray:
    a = array.new()
  
    你也可以选择增加一个删除对象操作:
    LUALIB_ITEM_DESTROY("del", MyArray )   // 删除MyArray
    这样,你就可以直接删除一个对象了:
    array.del(a)

  (3)  BEGIN_REGLUALIB_MEMBER() …END_REGLUALIB_MEMBER()
    在此处,你可以定义对象的成员函数,也可以重载对象的操作符——是的,就象C++的operator重载。例如:
    LUALIB_ITEM_FUNC("__newindex", void (MyArray*, int, double), &MyArray::setvalue)
    就是重载 operator[] 操作符。Lua中可重载的操作符还有许多,如:

    __getindex:操作符[],支持读取访问,如 v = a[10]
    __newindex:操作符[],支持赋值访问,如 a[10] = 1.22
    __tostring:将变量转换成字串__add:等同于operator +
    __add:操作符 +
    __sub:操作符 –
    __mul:操作符 ×
    __div:操作符 ÷
    __pow:操作符 ^ (乘方)
    __unm:一元操作符 –
    __concat:操作符 .. (字符串连接)
    __eq:操作符 == (a ~= b等价于 not a == b)
    __lt:操作符 < (a > b 等价于 b < a)
    __le:操作符 <= (a >= b 等价于 b <= a,要注意的是,如果没有定义"__le",则Lua将会尝试将a<=b 转换成 not (b < a) )

    __gc:在垃圾回收时调用此函数,相当于C++的析构函数。强烈建议定义此操作符,以免造成内存泄漏等情况。比如:
    LUALIB_ITEM_DESTROY("__gc", MyArray )   // 垃圾收集时消除对象用。

    (注) 这里要说明一下,在lua中,访问索引操作符是__index,不是__getindex,在luaWrapper库中,为了方便使用,将其映射为__getindex,同时,对__index的定义将会被忽略。

    就这么简单。假如你已经有现成的类,而你没有修改该类的权力,如何将其加入到Lua中呢?答案就是,继承它,将把派生类加入到Lua中。

结束语
  LuaWrapper 需要用到boost库的支持:boost/type_traits.hpp, boost/function.hpp, boost/bind.hpp,它使用了C++的模板部份特化,因此,C++编译器如果不支持此特性,将无法编译。目前支持此特性的编译器已经有很多。在VisualStudo产品系列中,只有VC7.1能支持此特性,因此,您如果正在使用VisualStudio,请确认你用的是VisualStudio2003。
  如果你觉得 LuaWrapper For C++ 能够帮助你,我会感觉很荣幸。我很愿意将这个程序库分享给大家。顺便一提的是,如果你在使用过程中发现BUG,或是有好的建议,希望您能与我联系。你在使用过程中,请不要删除文件中的署名信息;如果你修改了程序库,请您在修改的文件中加入您的修改说明。当然,我会非常欢迎您能将修改后的程序回馈给我。我会继续优化并完善它。

=============================================================

File: Click Here Download: LuaWrapper For C++
File: Click Here Download: LuaWrapper test program

=============================================================

  1. 很久以前,那还是我用win98的时候有次我系统崩溃了,因为我是电脑白吃,我朋友给我介绍了一个高手来帮我修电脑。

他看了一下电脑,问我有没有98的盘,我说没有。

他想了一下,叫我把固定电话拿给他,我想修电脑要电话干什么,但人家是高手,我也不好说什么,就把电话拔下来给他了。

他把电话线空着的一头接在电脑的一个插孔内,然后进入了dos,然后就开始在电话上不停的按着键,他按键的速度非常快,但是只按0,1两个键,我搞不懂这有什么用,但也不敢问,看了半个多小时,他还是不停的按这两个键,我渐渐的有些困,我问他这东西要搞多久,他说要几个小时,我给他倒了杯茶,就一个人去隔壁睡觉了。

醒来的时候,一看已经过了4个多小时,我起身到隔壁,看见他正在98里面调试,过了一会儿,他说,你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,谢过人家就走了。

后来我慢慢对电脑有了了解,终于了解,原来当时那位高手是用机器语言编了一个98系统,我后来问我朋友那位高手的下落,我朋友说前几年去了美国之后,杳无音讯….

  1. 很久以前,那还是我用winXP的时候有次我系统崩溃了,因为我是电脑白吃,我朋友给我介绍了一个高手来帮我修电脑。

他看了一下电脑,问我有没有XP的安装盘,我说没有。

他想了一下,叫我把一张空的DVD刻录盘和一根针拿给他,我想修电脑要刻录盘和针干什么,但人家是高手,我也不好说什么,就把DVD刻录盘拿一张来给他了。

他把针头对着刻录盘戳,他戳的速度非常快,但是只戳深或浅,我搞不懂这有什么用,但也不敢问,看了半个多小时,他还是不停的戳着DVD刻录盘,我渐渐的有些困,我问他这东西要搞多久,他说要几个小时,我给他倒了杯茶,就一个人去隔壁睡觉了。

醒来的时候,一看已经过了4个多小时,我起身到隔壁,看见他正在Xp里面调试,还装上了office、photoshop、迅雷、魔兽世界等软件……过了一会儿,他说,你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,谢过人家就走了。

后来我慢慢对电脑有了了解,终于了解,原来当时那位高手是针头刻了一个单面双层的DVD,在里面刻上了Xp、office、photoshop、魔兽世界的安装程序,我后来问我朋友那位高手的下落,我朋友说前几年去了美国之后,杳无音讯….


  1. 就在最近,我刚装上的Vista突然自己崩溃了,虽然我会写很多程序,但是Vista崩溃了我也没有办法,我朋友给我介绍了一个高手来帮我修电脑。

他看了一下电脑,问我有没有Vista的安装盘,我说没有。

他想了一下,叫我拿一根没用的网线和一把剪刀,我想修电脑要网线和剪刀干什么,但人家是高手,我也不好说什么,就拿了一根没用的网线和一把剪刀给他。

他把网线一头戳到网卡上,剪断另外一头,然后就在哪里不停的拨弄那八根线。他拨弄的速度非常快,但是拨弄得线总是不一样,我搞不懂这有什么用,但也不敢问,看了半个多小时,他还是不停的拨弄着网线。我渐渐的有些困,我问他这东西要搞多久,他说要几个小时,我给他倒了杯茶,就一个人去隔壁睡觉了。

醒来的时候,一看已经过了4个多小时,我起身到隔壁,看见他正在Vista里面调试,还装给装好了Visual Studio 2005 Team SuitMs SqlServer 2005 Enterprise EditionBT,还边下载边看American片……过了一会儿,他惊觉我站在后面,不好意思地对我说,你试试。我坐上椅子用了一下,真的好了,我当时太震惊了,整个人傻在哪里,谢过人家就走了。

后来我读到了前面那两篇文章,终于醒悟,原来当时那位高手是用网线模拟网络启动,下载了整个Vista,还有Visual Studio 2005 Team SuitMs SqlServer 2005 Enterprise EditionBT,以及他正在看的American片,我后来问我朋友那位高手的下落,我朋友说前几年去了美国之后,杳无音讯了十几年,期间也回过中国两次,最近这一次回来说,干这个太没意思了,回来收拾收拾准备去阿尔法半人马座,他说他也听不懂那高手说什么,只是最近传闻微软的Vista全世界都无法激活,估计跟他的离去有关,当然了,这几天也是杳无音讯….

注意力的集中作为一种特殊的素质和能力,需要通过训练来获得。那么,训练自己注意力、提高自己专心致志素质的方法有哪些呢?  

方法之一:运用积极目标的力量  

         这种方法的含义是什么?就是当你给自己设定了一个要自觉提高自己注意力和专心能力的目标时,你就会发现,你在非常短的时间内,集中注意力这种能力有了迅速的发展和变化。  

         同学们要在训练中完成这个进步。要有一个目标,就是从现在开始我比过去善于集中注意力。不论做任何事情,一旦进入,能够迅速地不受干扰。这是非常重要的。比如,你今天如果对自己有这个要求,我要在高度注意力集中的情况下,将这一讲的内容基本上一次都记忆下来。当你有了这样一个训练目标时,你的注意力本身就会高度集中,你就会排除干扰。  

         同学们知道,在军事上把兵力漫无目的地分散开,被敌人各个围歼,是败军之将。这与我们在学习、工作和事业中一样,将自己的精力漫无目标地散漫一片,永远是一个失败的人物。学会在需要的任何时候将自己的力量集中起来,注意力集中起来,这是一个成功者的天才品质。培养这种品质的第一个方法,是要有这样的目标。  

方法之二:培养对专心素质的兴趣  

         有了这种兴趣,你们就会给自己设置很多训练的科目,训练的方式,训练的手段。你们就会在很短的时间内,甚至完全有可能通过一个暑期的自我训练,发现自己和书上所赞扬的那些大科学家、大思想家、大文学家、大政治家、大军事家一样,有了令人称赞的注意力集中的能力。  

         同学们在休息和玩耍中可以散漫自在,一旦开始做一件事情,如何迅速集中自己的注意力,这是一个才能。就像一个军事家迅速集中自己的兵力,在一个点上歼灭敌人,这是军事天才。我们知道,在军事上,要集中自己的兵力而不被敌人觉察,要战胜各种空间、地理、时间的困难,要战胜军队的疲劳状态,要调动方方面面的因素,需要各种集中兵力的具体手段。同学们集中自己的精力,注意力,也要掌握各种各样的手段。这些都值得探讨,是很有兴趣的事情。  

方法之三:要有对专心素质的自信  

         千万不要受自己和他人的不良暗示。有的家长从小就这样说孩子:我的孩子注意力不集中。在很多场合都听到家长说:我的孩子上课时精力不集中。有的同学自己可能也这样认为。不要这样认为,因为这种状态可以改变。  

         如果你现在比较善于集中注意力,那么,肯定那些天才的科学家、思想家、事业家、艺术家在这方面还有值得你学习的地方,你还有不及他们的差距,你就要想办法超过他们。  

         对于绝大多数同学,只要你有这个自信心,相信自己可以具备迅速提高注意力集中的能力,能够掌握专心这样一种方法,你就能具备这种素质。我们都是正常人、健康人,只要我们下定决心,不受干扰,排除干扰,我们肯定可以做到高度的注意力集中。希望同学们对自己实行训练。经过这样的训练,能够发生一个飞跃。  

方法之四:善于排除外界干扰  

         要在排除干扰中训练排除干扰的能力。毛泽东在年轻的时候为了训练自己注意力集中的能力,曾经给自己立下这样一个训练科目,到城门洞里、车水马龙之处读书。为了什么?就是为了训练自己的抗干扰能力。同学们一定知道,一些优秀的军事家在炮火连天的情况下,依然能够非常沉静地、注意力高度集中地在指挥中心判断战略战术的选择和取向。生死的危险就悬在头上,可是还要能够排除这种威胁对你的干扰,来判断军事上如何部署。这种抗拒环境干扰的能力,需要训练。  

         我在你们这么大的年纪时曾有意做过这种训练。就是不管环境多么嘈杂,当我进入我要阅读和学习的科目时,对周围的一切因素置若罔闻。这是可以训练成功的。  

方法之五:善于排除内心的干扰  

         在这里要排除的不是环境的干扰,而是内心的干扰。环境可能很安静,在课堂上,周围的同学都坐得很好,但是,自己内心可能有一种骚动,有一种干扰自己的情绪活动,有一种与这个学习不相关的兴奋。对各种各样的情绪活动,要善于将它们放下来,予以排除。这时候,同学们要学会将自己的身体坐端正,将身体放松下来,将整个面部表情放松下来,也就是将内心各种情绪的干扰随同这个身体的放松都放到一边。常常内心的干扰比环境的干扰更严重。

尝试着这样集中注意力

1、如果你对即将要完成的工作或学习缺乏兴趣,可以设立“期限效果.”。一般人做事时,以刚开始与快结束时效率最高。在两者之间,将会出现效率低落、缺乏注意力的现象,心理学上称之为“中间松懈”,了维持注意力,可以象马拉松选手一样,设立中间站,更能发挥全力。中途站的时间不能太长也不能太短,否则会带来反效果。

2、利用奖惩机制。把繁重的工作分成几段,每段之间达到目标都给予外发动机—报酬,对增进注意力有一定帮助。有时,惩罚也能收到良好的效果。比方说,先假设一个强劲的对手,为打败这个对手就需要不断鼓励自己。在高中阶段,正处于青春期,常回碰上莫名的烦恼,想些无聊的问题,以致无法专心用功。要改善这种情形,可以班上一名成绩优异的同学作为对手。

3、摘要重点有利于集中注意力。日本的武见太郎先生在学生时代,经常比教授先读完教材,而他阅读原书的方法与不同,专挑页数少的读,在彻底读完一本书后,一定整理归纳书中的问题与重点,将精华摘录下来,因此他的读书效率相当高。

4、利用积极的自我暗示。常持乐观的看法,是去除杂念、集中注意力不可缺少的要素。相信明天一定有好事,即使当天情绪低落,运气很差。

5、养成每天看小东西的习惯,称之为“凝视法”。应注意须以战胜眼前作用的气势凝视一点,随着凝视,意识会逐渐缩小到狭窄的范围内,使精神凝聚和高昂。首先,从身边的小东西挑选一种,如钢笔或橡皮等,一旦觉得厌烦时,马上闭上眼睛,在脑海中回想刚才见过的东西,并且要从个方面去描写。结束以后,便换另一种。这种方法可随时随地训练。

6、面对讨厌的事物要慢慢地引导。导致精神不集中的最大原因之一,就是缺乏欲望。有个典型的例子。有个人对英语一点也不感兴趣,一看到就头痛。老师对他提出建议;最初一个月,一天花5分钟打开课本,即不用读也不用写。接着第二个月,一天花10分钟,把书上的英文抄在笔记本上。再接着一个月,一天花20分钟,认真读课文。在这段期间,也不勉强他上课,完全随他的意思。结果他慢慢感伤其他学生,最后英文成绩竟然居全校之冠。

7、事先把闹钟调到限定的时间上。事先有个计划,然后把闹钟拨到指定的时间,然后开始做功课。这时,应把闹钟放在自己看不到的地方。刚开始时还可知道大概时间,但随着愈接近结束时间,心也愈紧张,不知道时间而时间又紧迫的情形下,便能发挥注意力,一口气干到底。但不可常常利用,一天利用一次即可。

8、看书或听课时,抱着向别人说明内容的态度,以提高注意力。周围的人都可成为训练自己注意力的桥梁。

①善于用心理因素集中注意力。首先,把没处理的杂事,记在备忘录上,则心理上就会感到轻松。其次,暂时抛开人际关系可提高注意力。要想高效率地完成工作、学习,必须有属于自己的时间,而不要单纯只为别人活着。再次,摒弃依赖心理,方能全力以赴。自己能够独立做的事,一定要自己去做,如果你在做某件事时,总想着一定会有人来帮忙的话,则就不会尽全力去完成它,也就不会有效率可言。

②以形象控制来集中注意力。时常想着自己工作时的形象,相信自己工作、学习时的形象能加强注意力。

③利用代换效果集中注意力。首先,以其它事情来缓解精神。无法集中精神时,可以先做一些简单的事情,借此来消除杂念,稳定情绪,从而集中注意力。其次,在学习、工作之前,去做好各种准备,做好准备工作,可以缓和心情、排除杂念,帮助你集中精神。最后,先做喜欢的事情并彻底做完,则对讨厌的事情也容易集中精神。

④多种动作协调起来以集中注意力。在学习时,手、眼、口全部动起来,则易于抑制疲劳,提高学习效率,增进记忆能力。

作者:叶依青

好像我说这句话会得罪大部份中国人,毕竟中国人的习性决定了他们不能接受忽然的指责。就像柏杨当年写《丑陋的中国人》时,虽然指责的人大部份也认同他的观点,但强烈的自尊心(或者说是自卑感),让他们不愿意承认。沉默就是默认,自尊的中国人当然不能选择沉默,所以那一段时间我真为柏杨担心,可知道堂堂中华十三亿人口呀,一人一口水,也能将柏杨十八代祖宗都能淹死。但柏杨还是没死,当然不是俺中华子孙手下容情,而是因为海峡太深,最多的口水也涨不起东海的波涛。所以我羡慕柏杨,因为我还不知道我此文一出后,会引来多少的口水。

我没有柏杨的才华,也没有柏杨的勇气,虽也想拿我的同胞中国人开涮,但毕竟心有余悸,所以还是选了比较中性的字眼——自卑。不管俺们中国人愿不愿意承认,自卑确实是中国人的本性,也是中国人最大的弱点。无根的民族美国人总喜欢说:“我骄傲,因为我们拥有世界上最强大的国家与最富裕的生活。只要我们美国人想办的事,就没有不可能的”。登星访月在他们还是梦想的时候,他们就没有说过这是不可能的。而中国人呢?同样的一句话,他会说:我们也许。。,可能。。。

骄傲在中国人的字典里是贬义词,而在西方却是褒义词。为什么呢?中国人习惯于中庸与谦虚。将那种束缚人性的谦虚当成美德,那么人性的灵感又何从发挥?如果不是将自已当成最好,又如何有勇气去挑战新的高度?好象中国人不知道这点,因为中国人自古以来习惯于谦虚。久之久之,也就默认了自已是不可能超越别人的,既然无法超越别人,那么除了自卑还能有什么呢?

崇洋媚外这句话也许是中国人的发明,但也恰好证明了中国人的自卑。外国的月亮就是比中国圆,这是某些海外归来的黑头发黄皮肤的中国种说的话,是耶非耶,不言而喻。如果说他们的话是无知,那么为何还有那么多人去肯定呢?

一件衣服一样电器,为何在中国市场上一贴牌身价就翻了好几番呢?可知道这同样是出自中国的工厂呀。这里的原因不说也明了,因为某些中国人本身就瞧不起自已,总是觉得自已生产的东西比不上外国人的。就是知道是本土贴牌,也会振振有词的说外国人的验收标准是何等的高级,以此来证明中国人的东西本身就是不良产品。此等的自卑,就是西装革履又如何去掩盖自已卑微的灵魂呢?

小人物的自卑我们可以将之归之为生活的重负,让他不得不自卑。大人物的自卑我们也可以归结为他们的无知。那么作为人类灵魂工程师的教师呢?他们又如何生搬硬套的将西方的教育经验搬来,还谓之为世界上最先进的教育方式。可是再回头一看,结果呢?老天,还是培养不出如外国人一样的天才。为何?土壤不同吗,不一样的土壤当然会生出不同的果。淮南的桔淮北的枳,等到真正的认识了才知道没有自已的创新是学不来外国人现成的东西的。

这样的事例比比皆是,打开电视,全是海外的舶来秀产品。弄三两花瓶在那里自娱自乐,还谓之为与世界接轨。就如近段时间疯起的真人秀,我就是怎么也看不出《地球故事》的那股子味道。为何,还不是因为别人剩下的冷饭总是坏人胃口的。再看那些娱乐时尚杂志,介绍的要么是海外的红男绿女,要么就是巴黎米兰的时尚,一个个怪模怪样的时尚,我真怀疑适不适合中国人瘦小的身躯。我常常想,做一个中国人真的很幸福,全世界的优秀我都能看到。不过也有一丝悲凉,那就是中国人在那里?在中国看不到中国人的东西,这本身难道不是一种悲哀吗?

近几年中国人最津津乐道的是什么?那就是在外宇宙能看到的地球唯一建筑物就是万里长城。就先不说看得到看不到,就是看到了又如何,又能证明什么?难道中国人只会沉浸在过去的辉煌吗?外国宇航员说在外太空根本见到任何的地球建筑物,咱中国人不信,可当中国人自已的宇航员杨利伟还说没有见到时,在场的中国记者都惊愕当场。为何?因为就那么一点虚荣也被正直的杨利伟打破时,他们那自卑的心理就原形毕露了。用得着吗?中国人。万里长城的雄伟不是外太空就能证明的,就象杨利伟说了见不到时,也没有一个外国人不说中国的万里长城伟大呀,为何我们中国人就不能接受这个事实呢。对于这种强烈的虚荣,我们除了用自卑解释,我们还有别的解释吗?

万里长城此事未了,现在又冒出了个美洲印地安人是中国种。引经据典,旁证博论,好象不证明跟咱中国人搭点界不甘心似的。大清朝无能,派出公使求证是为了振奋疲弱的民心。而现在这种证明又能带给我们什么实质的好处呢。看着津津乐道的那些人满脸的骄傲样,我真担心,如果不能彻底的证明是中国种的话,他们又将陷入怎样的自卑。

我还看到过一篇一个上海女大学生写给中国男人的一封信,在信中她说她宁愿做日本人的性奴隶也不愿做中国男人的老婆,因为中国男人太窝囊。我不想去求证中国男人到底怎么样,但有一点我想说的是,一个人连自已也瞧不起自已,那她又怎么还有一点自尊呢。也许她此刻说不定已经做了日本人的奴录,正用她卑微的灵魂享受着她特有的奴性。想嫁就嫁外国人,好自卑的人性哟,为何在中国竟有那么大的市场呢?

凡此种种,不枚胜举。说穿了也就是自卑在作怪,因为自卑所以忘却尊严,而没有尊严的人就会更加自卑,这也就造成了一个恶性循环,造成了中国人缺乏自信,没有勇气在世界民族之林中笑傲风云。

谦虚是中国人的美德,而过份的谦虚就是骄傲这句话是行不通的。准确的说,应该是过分的谦虚就是自卑。事实也证明了我的论调,你可见过中国人有几个骄傲的仰着脑袋走路的。

仰起头挺起胸是我们走向骄傲的第一步,因为只有仰起头,你才能忘记脚下那片土地的束缚,而让自已的空间跟长空般的广阔,看到这个世界原来竟是有那样大的空间供自已发挥,灵魂也可以在那里自由的驰骋从而冒出创新的火花。

中国人,请别自卑,你没有理由自卑的。你有曾经灿烂辉煌的文化,你的影响力曾遍布全球,这一点难道不是我们骄傲的理由吗?为何我们要谦虚,因为我们的优秀,可以让我们骄傲的说:我,中国人,无论是始祖还是现在,我们都在为这个星球贡献着自已最大的力量。有付出当然有骄傲的理由,而且正因为我们骄傲,所以我们总是逼着自已创新以证明我们的骄傲不是平空想像的。

中国人,请不要自卑。纵使我们不是现在世界上最强大的国家,但只要我们自信的去面对,坦然的接受自已的不足正视自已的缺点,不为虚拟的虚荣去自豪,埋头苦干,不卑不亢,那么我相信凭中国人特有的聪明与才智在不久的将来,我们就可以像我们的先祖一样骄傲的说:中国,中央之国,万邦朝圣之地,大中华的恩泽遍布全球。。。。。。

自卑与自信,虽是一字之差,可结果往往谬之千里。中国人,你想选择那一样呢?

五千年的文明古国,领先世界文明三千年的华夏,经历过沉沦正在重现辉煌的神洲。我想问一句:中国人,你为何还要自卑?你有自卑的理由吗?

作者:scottier   

剑道, 只是我从小到大从各种各样的媒体中得出的印象.   

因为喜欢剑术, 所以想做一个剑客. 在还没有做剑客时, 就想像自己有一把好剑, 威风凛凛地站在风中, 身边的树叶飘飘......, 一幅很酷的样子. 做了剑客后发现, 想找一把好剑不容易, 于是, 行侠江湖的几年中, 在找一把好剑.
  
终于过了几年, 有点胡子了, 有个和尚或老道看你有几分仁义的样子, 指了个好剑的方向. 结果, 你以前做梦都在想像的举剑一睹出现了, 当然, 是给自己看的, 注意, 要披一件大衣, 找个风口, 别忘了边上该有很多枯叶.POS摆完, 也该练点真功夫了, 要不真浪费了这么多年的追求, 又被别人数落个"金玉其表"的评价. 于是你练呀练呀, 总算有一天, 有一个你不认识的人叫了你一声"高手". 听第一遍时你还以为他在叫别人. 那天晚上你睡觉都在偷笑.   

然后, 你发现与人交手时, 的确顺手了很多. 每与一个曾经的对手交手后,你的信心都更增加一份. 你非常的爱护那把剑, 所以你常在无人的时候, 在林子里舞剑, 学习怎样提高你的剑气. 日子又过了很久, 你的剑气与日俱增,现在, 江湖中到处传颂你那"威风凛凛地站在风中, 身边的树叶飘飘......,一幅很酷的样子". 很多的小年青因为你, 也立誓想做一个剑客.   

终于有一天, 你发现自己很无聊. 在你眼中, 天下已没有什么高手了, 而自己的剑术好像也没有办法提高了. 你只是觉的闷, 奇怪天下居然还有你能做到头的事, 但是你隐约中又觉的自己还差一些. 你非常的爱护那把老剑, 你已能做到人剑一体了. 只有在一个人舞剑时, 你才能略感欣喜. 那种当初寻剑的回忆,那些练剑的回忆, 那些护剑的回忆, 在舞剑时, 一幕幕地划过脑海. 在别人看来, 你已是天下第一剑客了, 你的举手投足无不说明, 你是天下第一的. 直到有一天早上醒来, 你发现剑没了.这种事总是传的很快, 那些平日里惧怕你的对手, 又开始在江湖中兴风作浪起来了, 有几次你与他们偶遇上时, 几乎拼的要死, 才逃走. 失去了剑等于失去了你自己. 江湖上到处都在找那把你失去的剑. 又是一场无止境的血战. 你突然觉的非常的无趣, 原来练剑一辈子, 剑一丢, 等于前功尽弃. 你找个安静的山谷, 想修隐起来, 人生哪, 真是一个没有定数的人生哪. 你在那谷儿中看鱼虫跃, 鸟儿飞, 一直想找到自己失剑后会退步的这么惨的原因.   

有一天, 你像往常一样出游, 在谷中闲逛, 有一只蛇从后面飞速地刺向你, 你在惊觉中, 猛地转身, 用手一挥, 眼前出现的是, 蛇被劈成了两截. 你顿悟,剑术中差的那一点正是"有剑似无剑, 无剑似有剑". 原来, 那把好老剑, 正是你在习剑中不可逾越的一个障碍. 你不禁感叹: 人生哪, 真是奇妙的人生哪.

你总算是悟到了剑道.

程序, 现在是我的职业, 一个差不多快被世间无数热爱或不热爱的人做烂的职业.   

因为喜欢程序, 所以想做一个程序员. 在还没有做程序员时, 就想像自己有一台好机子, 用着牛B的C/C++, 十指飞快地在键盘上移动, 最后一个大回车.....看到程序运行起来时, 很满足的样子. 做了程序后发现, 想有一台好机子, 想找一个好公司不容易, 刚习程序时, 编译环境最好是字符或图形化的, 在命令行上敲make, cc带一堆的参数, 真的很恐怖. 后来到了 WINDOWS下, 当然是VC,把工具栏上的按键设成自己有用的几个, 开个全屏模式, 最大化地利用屏幕,多爽. 可是机子是公共的, 有时候累了往边上一躺, 就听到同事蹑手蹑脚地在你的位置坐下, 按了几下MOUSE, 开始打FF8. 休息了一阵, 有了感觉, 想爬起来开工, 但一看到同事那幅沉醉的样子, 偶而还转过头来, 冲你一笑, 报歉地说这儿没有存盘点. 唉, 不容易, 谁叫他那台机子WINDOWS下只剩500MB的空间,而且只是6326的显卡呢. 又躺在了床上, 想着, 要是有一台机子多好, 没有一点空间是冗余的, 都只有我想要的东西装在硬盘里面, 什么时候有灵感, 什么时候就爬起来开工. 于是, 程序的几年中, 在找一台好机子.   
终于过了几年, 有了自己的笔记本, 真爽啊, 可以随便移动, 想在哪写就在哪写.装UNIX 装LINUX装WINDOWS装上C/C++, 笔记本摆在哪儿都不一样, 摆在破桌上,也让人想到一张美国西部"破桌, 笔记本, 通辑犯的海报, 手枪", 很浓的金黄色调的宣传画. 真酷. POS摆完, 也该练点真功夫了, 要不真浪费了这么多年的追求, 又被别人数落个"金玉其表"的评价. 于是你练呀练呀, 总算有一天, 有一个你不认识的人叫了你一声"高手". 听第一遍时你还以为他在叫别人. 那天晚上你睡觉都在偷笑.   

然后, 你有新任务时, 的确顺手了很多. 以前要用一天写的代码, 现在只要一个早晨了, 每完成一个完整的程序, 你都更增添了一份信心. 你不知疲倦地用C/C++写和各种各样的程序, 你坚信, 只有用C/C++的程序员才是真正的程序员. 你有点蔑视VB, DEPHI, 用它们写程序, 光是启动程序都让你觉的无法忍受, 更别提那些不简练的语法了. 用C/C++的水准越来越高, 你几乎可以用它来做任何事, 加上一点硬件, 你让你家的电饭堡每天6点开煮, 电视晚上7点开播, 还有指纹锁......有一天有一个你倾慕已久的水瓶座MM去你那儿玩, 被你家的半自动化所折服, 惊叹的爱上了你.   

终于有一天, 你发现自己很无聊. 在你眼中, 程序没有什么新意了, 无非就是空间与时间的平衡, 而自己的程序好像也没有办法提高了. 你只是觉的闷, 奇怪天下居然还有你能做到头的事, 但是你隐约中又觉的自己还差一些. 你非常的爱护那台赛扬老机, 你已能做到人机一体了. 只有在用它写自己想玩的游戏时, 你才能略感欣喜. 那种当初学C/C++的回忆, 那些攒机的回忆, 那些护机的回忆, 在键入{}时, 一幕幕地划过脑海. 在别人看来, 你已是天下第一程序员了,你的举手投足无不说明, 你是天下第一的. 直到有一次旅游回来, 你发现屋子被撬了,没有一样东西留下.这种事总是很影响你的程序思路的, 那些平日里出现的问题, 又开始在新的任务中使你痛苦万分了, 有几次你与它们偶遇上时, 几乎想的要死, 才想出来解决之道. 你常在用程序的过程中想用一个以前写的工具时, 却发现没有了, 要重头写过. 那种累的感觉一下子冲上心头, 但是你又不想用别人写的东西, 你根本不相信别人写的东西里面没有BUG. 你觉的非常的无趣, 原来程序一辈子, 机子一丢, 等于前功尽弃. 你辞了工作, 想休息一阵, 人生哪, 真是一个没有定数的人生哪. 你路过图书馆时, 看着那些年青的大学生直直地盯着屏幕,脸上 整一幅痴迷的表情, 脑中一直想找到自己失机后, 写程序变的举步为艰的原因.   

有一天, 你在大学中闲逛, 你走在两个看起来像是大二的男生后面, 他们中的一个听起来水平更高一些, 因为他可以自己用C写一个俄罗斯方块, 另一个说: 哇塞, 这么利害, C语言我一直觉的很难, 像那些指针什么......", 水平更高一些的说: 我觉的你才利害, 用QUICK BASIC 那么不方便的东西都可以写出俄罗斯方块......". 你顿悟, 是呀, 你一生追求只用C/C++, 是为了什么? 程序只是用来解决实际问题的. 你不必拘泥在一种语言中而看不起别的语言, 你不必拘泥在自己的程式中而不用别人的工具呀. 原来, 语言成了你最大的障碍, 你不禁感叹: 人生哪, 真是奇妙的人生哪.   

你又找了份工作, 我们后来看到, 你在任何一台电脑中, 任何一种环境下, 任何一种语言, 甚至有时不用编程, 都可以解决所面对的任何问题.

你总算是悟到了编程之道.

初用Ubuntu感觉还不错,但发现没有RMVB的播放软件,下载个安装不上。
后来才发现, 不是有效的可执行文件。
转换之:
chmod +x RealPlayer10GOLD.bin
之后一路回车就OK了。
./RealPlayer10GOLD.bin


装完毕,在“应用程序”菜单中的“影音”中就出现了RealPlayer的菜单,结果一点反应也没。
发现是与SCIM冲突了,解决办法是,打开Realplayer的启动文件,这是一个shell脚本(默认位置为*(安装路径)/RealPlayer/realplay),直接在以#!/bin/sh开头的第一行下面(也就是第二行)添加下面语句:
export GTK_IM_MODULE=xim

按住Alt,用小键盘输入3到4个数字,再放开Alt键。
Symbol
Numeric Code
Symbol
Numeric Code
0134
·
0183 (250)
0135
¹
0185 (251)
0137
¼
0188 (172)
0149 (248)
½
0189 (171)
0153
¾
0190 (243)
§
0167 (245)
×
0215 (158)
¨
0168 (249)
ß
0223 (225)
©
0169 (184)
÷
0247 (246)
®
0174 (169)
ƒ
(159)
°
0176 (248)
º
(167)
²
0178 (253)
¿
(168)
³
0179 (252)
¢
(189)
µ
0181 (230)
¤
(207)
0182 (244)
±
(241)
还有很多没全试。

汤以仁厚收揽人心,争取人民的支持,有一次,他外出游玩,看见一人在树上挂起一张网,然后喃喃自语说:“不论天上来的,还是地面来的,凡是从四面八方来的鸟,都飞进网里来。”汤对他说:“你太过分了吧,怎么可以这样网尽杀绝呢!你撤掉三面,留下一面的网就可以了。”农民依言照办。汤祝告道:“鸟儿啊,你们愿意往左的就往左,往右的就往右,只有不听我话的鸟儿,才飞进网里来。”汤网开三(应该是一面吧,估计原作者写错了)面,恩及禽兽的事传开后,人民都称赞他对待百姓宽厚仁慈,纷纷拥护,汤的势力进一步壮大。


汤:汤,姓子,原名履,又称武汤、成汤、商汤、天乙、天乙汤,甲骨卜辞中称作唐、成、大乙、天乙、金文和周原甲骨文中称作成唐。汤是契的十四代孙。生卒年不详,商部落首领,灭而建商。在位13年,病死。葬处据传有六处,说法最多的是在毫(今河南省-商丘县北面)。

点绛唇
                          寇平仲(寇准)
水陌轻寒,社公雨足东风慢。
定巢新燕。
湿雨穿花转。
象尺熏炉,拂晓停针线。
愁蛾浅。
飞红零乱。
侧卧珠帘卷。

——————————-
寇准–刚直足智 功高名重  2003-6-21 寇准(961—1023),北宋政治家。字平仲,华州下邦(今陕西渭南)人。太平兴国进士。淳化五年(994年)为参知政事。景德元年(1004年)拜相。辽兵进攻宋朝时,他力排众议,主张坚决抵抗,促使真宗亲往澶州(今河南濮阳)督战,与辽订立澶渊之盟。不久被王钦若排挤罢相,出任陕州知州。晚年又被起用,天禧四年(1020年)又遭丁谓陷害,后被贬至雷州(今广东海康),卒于贬所。


也许自己想做的就是这样的人,努力之。

被 传下来的词就这几首。

甘草子

春早。柳丝无力,低拂青门道。暖日笼啼鸟。初坼桃花小。遥望碧天净如扫。曳一缕、轻烟缥缈。堪惜流年谢芳草。任玉壶倾倒。

踏莎行

春色将阑,莺声渐老。红英落尽青梅小。画堂人静雨濛濛,屏山半卷馀香袅。密约沈沈,离情杳杳。菱花尘满慵将照。倚楼无语欲销魂,长空黯淡连芳草。

阳关引

塞草烟光阔,渭水波声咽。春朝雨霁轻尘歇。征鞍发。指青青杨柳,又是轻攀折。动黯然,知有后会甚时节。更尽一杯酒,歌一阕。叹人生,最难欢聚易离别。且莫辞沉醉,听取阳关彻。念故人,千里自此共明月。

点绛唇

水陌轻寒,社公雨足东风慢。定巢新燕。湿雨穿花转。象尺熏炉,拂晓停针线。愁蛾浅。飞红零乱。侧卧珠帘卷。

蝶恋花

四十年来身富贵。游处烟霞,步履如平地。紫府丹台仙籍里,皆知独擅无双美。

将相兼荣谁敢比。彩凤徊翔,重浴荀池水。位极人臣功济世,芬芳天下歌桃李。

江南春

波渺渺,柳依依。
孤村芳草远,斜日杏花飞。
江南春尽离肠断,苹满汀洲人未归。

VMware虚拟磁盘管理工具是VMware Workstation软件包里的一个软件,它让你用命令行或通过脚本来创建管理修改虚拟磁盘文件。它的一个重要的特性是能够增大虚拟磁盘大小,使虚拟磁盘的最大尺寸比刚创建时(定义的)更大。它是这样一种方法,如果你需要更多的磁盘空间在已经定义的虚拟机中,而你又不想添加另外一块硬盘或者用Ghost软件来传递虚拟磁盘上的数据到另外一块更大的虚拟磁盘上去时,你可以用改变虚拟磁盘最大尺寸方法来做。不过你可不能用这种方法对你的物理硬盘实施。
    另外一个功能是让你能够改变虚拟磁盘的类型。当你创建虚拟机时,你定义了虚拟磁盘空间的分配方式。你可以在以下选择一种分配方式:
- 所有的虚拟磁盘空间预分配。它相当于虚拟磁盘管理工具所说的预分配磁盘类型。
- 虚拟磁盘在开始时最小随着数据的增加而变大。它相当于虚拟磁盘管理工具所说的可增长磁盘类型。
使用虚拟磁盘管理工具,你能够更改虚拟磁盘的类型为预分配或可增长的、单个文件储存或每个文件大小为2GB的多文件方式。举个例子,你可以分配所有的虚拟磁盘空间,然后发现你需要收回一些主机上的硬盘空间。你能转换预分配的虚拟磁盘为可增长的虚拟磁盘,然后删除原来的那个虚拟磁盘文件。(这样)虚拟磁盘的大小将随着你的数据的增长而增长。
这些功能和使用脚本自动管理虚拟磁盘方法在VMware Workstation5.0版本中提供。
  你能够用虚拟磁盘管理工具完成以下任务:
- 使用脚本自动管理虚拟磁盘。
- 创建虚拟磁盘而不和任何一个虚拟机关联,举个例子,创建它作为样板。
- 在预分配和可增长的虚拟磁盘类型间进行转换。当你更改为可增长的虚拟磁盘类型,你就能够收回一些磁盘空间。你也能通过收缩虚拟磁盘来收回更多的空间。
- 增大虚拟磁盘的尺寸,使它比你创建时定义的尺寸更大。
- 磁盘碎片整理虚拟磁盘。
- 准备和收缩虚拟磁盘而不需要开启虚拟机进行(仅适用于Windows宿主机)。
你可以用虚拟磁盘管理程序管理由VMware GSX Server, VMware Workstation and VMware VirtualCenter(由GSX Server提供被VirtualCenter管理的虚拟磁盘)创建的虚拟磁盘。
  注意:你不能用虚拟磁盘工具创建物理磁盘。物理磁盘不能被虚拟磁盘管理工具或Workstation软件收缩。
  更多关于使用虚拟磁盘工具的信息,请阅读以下章节:
- 使用虚拟磁盘管理软件
- 使用虚拟磁盘管理实例

使用虚拟磁盘管理软件
    打开宿主机中的命令行或终端(Linux中的命令行),以运行虚拟磁盘管理软件。在Windows宿主机中,更改目录为你安装Workstation软件所在的目录。默认的安装目录为C:\Program Files\VMware\VMware Workstation。

  命令语法:
    vmware-vdiskmanager [选项]
  这里的选项你必须包含以下的一些选择项或参数
  选项和参数
  描述
<diskname>
    虚拟磁盘文件的名字。虚拟磁盘文件必须是.vmdk为扩展名。你能够指定一个你想要储存的虚拟磁盘文件的路径。如果你在你的宿主机中映射了网络共享,你也可以提供确切的虚拟磁盘文件的路径信息来创建虚拟磁盘在这个网络共享中

-c
    创建虚拟磁盘。你必须用-a, -s 和 -t 并指定选项参数,然后你需要指定所要创建的虚拟磁盘文件的文件名。

-r <sourcediskname>
    转换已经指定类型的虚拟磁盘的类型,结果会输出创建一个新的虚拟磁盘。你必须用-t选项来指定你想要转换成的磁盘类型,并且指定目标虚拟磁盘的文件名。
    一旦转换完成,你可以先测试虚拟磁盘以确保它能够像你所希望的那样工作,然后再删除原来的那个虚拟磁盘文件。
    为了让虚拟机重新认识转换后的虚拟磁盘,你应该使用虚拟机设置编辑器先从虚拟机中移除先前存在的虚拟磁盘,然后添加转换好的虚拟磁盘给虚拟机。

-x <n>[GB|MB] <diskname>
    增大虚拟磁盘到指定的容量。你必须指定新的更大尺寸的虚拟磁盘用GB或MB单位标示。你不能改变物理磁盘的大小。(废话)
  注意:在你运行虚拟磁盘管理软件前,你应该先备份虚拟磁盘文件。(因为不会创建新的文件,所以备份以防增大磁盘操作失败)

-d <diskname>
    对指定的虚拟磁盘碎片整理。你只能磁盘碎片整理可增长的虚拟磁盘。你不能磁盘碎片整理预分配的虚拟磁盘。

-p <mountpoint>
    为收缩磁盘做准备处理。如果虚拟磁盘被分成多个分区,每个分区必须被单独准备。分区(比如C:或D:)必须用VMware DiskMount工具映射。更多的应用VMware DiskMount映射和解除虚拟磁盘的映射的内容,请看VMware DiskMount用户手册,可以在VMware站点中http://www.vmware.com/pdf/VMwareDiskMount.pdf  下载到。VMware DiskMount免费软件也可以在http://www.vmware.com/download/ws/  页面下载到。
    在你对分区准备处理后,解除对此分区的映射。继续映射虚拟磁盘的其他每个分区,为收缩磁盘作准备处理直到完成虚拟磁盘上的所有分区的准备工作。
    你在同一时刻只能用VMware DiskMount映射虚拟磁盘的一个分区。你仅仅能在Windows宿主机上进行虚拟磁盘的收缩分区准备工作。

-k <diskname>
    收缩指定的虚拟磁盘。你只能够收缩可增长磁盘。你只能在Windows宿主机中(用这种方法)收缩虚拟磁盘。
    你不能够收缩有虚拟机快照的虚拟磁盘。你可以保持现有虚拟磁盘的状态,而用快照管理器删除所有快照。你也可以放弃自快照以来对虚拟磁盘所做的更改,恢复到快照时状态。

-a [ ide | buslogic | lsilogic ]
    指定磁盘适配器的类型。你在创建新的虚拟磁盘时必须指定其类型。选择以下类型之一:
    ide —— IDE接口适配器
    buslogic —— BusLogic SCSI接口适配器
    lsilogic —— LSI Logic SCSI接口适配器

-s <n> [GB|MB]
    指定虚拟磁盘的大小。确定大小用GB或MB做单位。你必须在创建磁盘时指定其大小。
    尽管你必须指定虚拟磁盘的大小,但当你增长它的大小时,你不能用-s这个选项。
    可以指定的磁盘大小规定:IDE和SCSI适配器都为最小100MB,最大950GB。

-t [0|1|2|3]
    你在创建一个新的虚拟磁盘或者重新配置一个虚拟磁盘时必须指定虚拟磁盘的类型。指定以下类型之一:
    0 —— 创建一个包含在单一虚拟文件中的可增长虚拟磁盘
    1 —— 创建一个被分割为每个文件2GB大小的可增长虚拟磁盘
    2 —— 创建一个包含在单一虚拟文件中的预分配虚拟磁盘
    3 —— 创建一个被分割为每个文件2GB大小的预分配虚拟磁盘

-q
    禁止虚拟磁盘管理程序写日志
    如果你允许记录日志,日志将会被虚拟磁盘管理程序产生并储存。在虚拟磁盘管理程序运行后,日志的名字和存放位置将会出现在命令行或终端中。

-n <source-disk>
    重命名指定的虚拟磁盘。需要指定命名后的虚拟磁盘名字。

使用VMware虚拟磁盘管理工具实例
    以下例子描述怎样使用虚拟磁盘管理工具。你需要在命令行中运行虚拟磁盘管理工具。

创建虚拟磁盘
    命令:vmware-vdiskmanager -c -t 0 -s 40GB -a ide myDisk.vmdk
    这个命令将创建一个40GB大小IDE接口的名字为myDisk的虚拟硬盘。虚拟磁盘包含在一个单一文件中。这个虚拟磁盘没有被预分配磁盘空间。
  实际命令输入:
    D:\Big Program Files\VMware\VMware Workstation>vmware-vdiskmanager -c -s 40Gb –a  ide -t 0 E:\myDisk.vmdk
  执行结果显示:
    Using log file C:\DOCUME1\AnEgg\LOCALS1\Temp\vmware-AnEgg\vdiskmanager.log
    Creating a monolithic growable disk ‘E:\myDisk.vmdk’
    Virtual disk creation successful.

转换一个虚拟磁盘
    转换一个预分配虚拟磁盘为可增长虚拟磁盘,用以下这个命令:
      vmware-vdiskmanager -r sourceDisk.vmdk -t 0 targetDisk.vmdk
    这个命令将转换磁盘从它的原始的预分配模式转变为包含在单一文件中的可增长虚拟磁盘。这个虚拟磁盘空间将不会被预先分配,虚拟磁盘工具将收回虚拟磁盘中的一些磁盘空间,而仅仅让里面的数据占用虚拟磁盘空间。
  实际命令输入:
    D:\Big Program Files\VMware\VMware Workstation>vmware-vdiskmanager -r “D:\WinXP
    SP2 V2.5\Windows XP Professional.vmdk” -t 0 “L:\Windows XP Professional.vmdk”
  执行结果显示:
    Using log file C:\DOCUME1\AnEgg\LOCALS1\Temp\vmware-AnEgg\vdiskmanager.log
    Creating a monolithic growable disk ‘L:\Windows XP Professional.vmdk’
    Convert: 100% done.
    Virtual disk conversion successful.

增大存在的虚拟磁盘的大小
    命令:vmware-vdiskmanager -x 40GB myDisk.vmdk
    这条命令将把虚拟磁盘myDisk.vmdk大小增大到40GB
  实际命令输入:
    D:\Big Program Files\VMware\VMware Workstation>vmware-vdiskmanager -x 40GB “D:\WinXP SP2 V2.5\Windows XP Professional S.vmdk”
  执行结果显示:
    Using log file C:\DOCUME1\AnEgg\LOCALS1\Temp\vmware-AnEgg\vdiskmanager.log
    Grow: 100% done.
    The old geometry C/H/S of the disk is: 8322/16/63
    The new geometry C/H/S of the disk is: 16383/16/63
    Disk expansion completed successfully.
    WARNING: If the virtual disk is partitioned, you must use a third-party
            utility in the virtual machine to expand the size of the
            partitions. For more information, see:
            http://www.vmware.com/support/kb/enduser/std_adp.php?p_faqid=1647
    创建好后,可以在磁盘管理中,看到未指派的空间。如果你想扩大磁盘分区的大小,可以用其他第三方软件来做。


#include "stdafx.h"
#include 
<stdio.h>
#include 
<stdarg.h>

int mul(int num,int data1,)
{
    
int total = data1;
    
int arg,i;
    va_list ap;
    va_start(ap,data1);
    
for(i=1;i<num;i++)
    {
        arg 
= va_arg(ap,int);
        total
*=arg;
    }
    va_end(ap);
    
return total;
}

long mul2(int i,)
{
    
int *p,j;
    p 
= &i+1;//p指向参数列表下一个位置
    long s = *p;
    
for (j=1;j<i;j++)
        s 
*= p[j];
    
return s;
}

int main()
{
    printf(
"%d\n",mul(3,2,3,5));
    printf(
"%d\n",mul2(3,2,3,5));
    
return 0;
}



printf的设计
#include "stdio.h"
#include 
"stdlib.h"
#include 
<stdarg.h>

void myprintf(char* fmt, )        //一个简单的类似于printf的实现,//参数必须都是int 类型
{
    
//char* pArg=NULL;               //等价于原来的va_list
    va_list pArg;
    
char c;
   
   
// pArg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对参数取址,而不是取值
  
// pArg += sizeof(fmt);         //等价于原来的va_start         
    va_start(pArg,fmt);

    
do
    {
        c 
=*fmt;
        
if (c != '%')
        {
            putchar(c);            
//照原样输出字符
        }
        
else
        {
//按格式字符输出数据
            switch(*++fmt)
            {
            
case 'd':
                printf(
"%d",*((int*)pArg));           
                
break;
            
case 'x':
                printf(
"%#x",*((int*)pArg));
                
break;
            
case 'f':
                printf(
"%f",*((float*)pArg));
            
default:
                
break;
            }
            
//pArg += sizeof(int);               //等价于原来的va_arg
            va_arg(pArg,int);
        }
        
++fmt;
    }
while (*fmt != '\0');
    
//pArg = NULL;                               //等价于va_end
    va_end(pArg);
    
return;
}
int main(int argc, char* argv[])
{
    
int i = 1234;
    
int j = 5678;
   
    myprintf(
"the first test:i=%d",i,j);
    myprintf(
"the secend test:i=%f; %x;j=%d;",i,0xabcd,j);
    system(
"pause");
    
return 0;
}


可变参数在编译器中的处理 

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面以VC++stdarg.hx86平台的宏定义摘录如下(’"’号表示折行):
typedef char * va_list; 
#define _INTSIZEOF(n) \ 
((
sizeof(n)+sizeof(int)-1)&~(sizeof(int- 1) ) 
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 
#define va_arg(ap,t) \ 
*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 
#define va_end(ap) ( ap = (va_list)0 ) 

    定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函数是从右向左压入堆栈的,(1)是函数的参数在堆栈中的分布位置.我们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再看va_start的定义,定义为&v+_INTSIZEOF(v),&v是固定参数在堆栈的地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址,如图:

高地址|-----------------------------| 
|函数返回地址 | 
|-----------------------------| 
|| 
|-----------------------------| 
|第n个参数(第一个可变参数) | 
|-----------------------------|<--va_start后ap指向 
|第n-1个参数(最后一个固定参数)| 
低地址
|-----------------------------|<-- &
图( 
1 ) 

    然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我们看一下va_argint型的返回值: j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回ap-sizeof(int)int*指针,这正是第一个可变参数在堆栈里的地址(2).然后用*取得这个地址的内容(参数值)赋给j.

高地址|-----------------------------| 
|函数返回地址 | 
|-----------------------------| 
|| 
|-----------------------------|<--va_arg后ap指向 
|第n个参数(第一个可变参数) | 
|-----------------------------|<--va_start后ap指向 
|第n-1个参数(最后一个固定参数)| 
低地址
|-----------------------------|<-- &
图( 
2 ) 

    最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcclinuxx86平台就是这样定义的.在这里大家要注意一个问题:由于参数的地址用于va_start,所以参数不能声明为寄存器变量或作为函数或数组类型.关于va_start, va_arg, va_end的描述就是这些了,我们要注意的是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的

声明:此文章翻译自DirectX 9.0C OCT 2006 SDK的Direct3D For C++帮助文档的Enabling Direct3D Debug Information主题,前面部分有些没有翻译的。

在#include <D3D9.h>语句前面加上以下这条语句即可:

1
#define D3D_DEBUG_INFO

关闭调试时查看D3D Device对象的情况

auto_analysis_step_ac

开启调试时查看D3D对象的情况

auto_analysis_step_ac

为外部调试使用调用堆栈

Direct3D 调试模式开启之后,你可以在任何时候查看对象的调用堆栈。这会导致你的程序非常慢,但是可以使用此功能检查资源泄漏(内存泄漏的一种)。开启调用堆栈,设置这个注册表键值为1即可:

1
\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Direct3D\\D3D9Debugging\\EnableCreationStack

开启D3D调试模式,然后重建你的工程,系统将会给你访问这个附加的变量:LPCWSTR CreationCallStack;

这个变量存储着每次创建对象时的调用堆栈。这会导致你的程序非常慢,但是可以使用此功能检查资源泄漏。

其实这个很简单,只是创建设备的时候那个hwnd有点不一样!
可能有人就会说,用GetDesktopWindows()获得桌面的句柄不就行了?那就错了!这样会没有效果的!正确的代码如下:

1
2
3
4
5
HWND hWnd = FindWindowEx(GetDesktopWindow(), 0, "Progman", "Program Manager");
hWnd = FindWindowEx(hWnd, 0, "SHELLDLL_DefView", 0);
hWnd = FindWindowEx(hWnd, 0, "SysListView32", "FolderView");
// 初始化 D3D 设备
InitD3D(hWnd);

是不是很简单!哈哈。

显示的时候,如果想渲染在桌面的一角,则可以这样写:

1
2
3
4
5
6
7
8
// 显示在左上角,128×128宽
RECT rect;
rect.left = 0;
rect.right = 128;
rect.top = 0;
rect.bottom = 128;
// 显示
g_pd3dDevice->Present(0, &rect, 0, 0);

CD3DApplication 框架类已经光荣的退役了,取而代之的是DXUT*系列函数。在这篇文章中将要简单介绍DXUT框架的应用。
最近的directx 下载地址 , 这里面包含了64位操作系统库以及directx10等等,directx10 的程序例子只能运行在windows vista 上面。尽管可以编译,但缺少directx3d10 运行库. 在安装sdk之后,别忘记安装Redist目录中最新的directx运行库。
在vc.net->tools->options中配置头文件以及库文件地址。

使用dxut框架进行directx设计时,拷贝directx sdk 安装目录\Samples\C++\中的Common文件内容到你的程序目录下(如下图)。

创建一个名为dxut 的windows apllication 空工程,然后在vc solution explorer 中项目下加入common目录(添加存在文件,注意dxsound两个文件没有加入),然后添加一个源文件main.cpp

然后在vc solution explorer 中项目上点右键进入项目属性页(property pages)加入附加的common目录作为附加头文件搜索目录,同样在属性页的 linker->input->addtional dependencies 中加入 dxerr.lib dxguid.lib d3dx9.lib d3d9.lib winmm.lib comctl32.lib 等链接库

main.cpp 源文件内容如下:

#include  < dxstdafx.h >
// --------------------------------------------------------------------------------------
//  当Directx3D设备被创建后,这个回调函数马上被调用,因为D3DPOOL_MANAGED资源在设备被销毁后
//  需要重新装载,这里是最佳创建D3DPOOL_MANAGED资源的地方,创建的资源应在 OnDestroyDevice 
//  函数中销毁 。
// --------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9 *  pd3dDevice,  const  D3DSURFACE_DESC *  pBackBufferSurfaceDesc,  void *  pUserContext )
{
 
return  S_OK;
}

// --------------------------------------------------------------------------------------
//  当direct3d设备被复位后,这个函数立即被调用,这里最好放置D3DPOOL_DEFAULT 资源代码,因为这
//  这些资源在设备丢失后需要重新装载。在这里创建的资源应该在OnLostDevice 函数中释放
// --------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9 *  pd3dDevice,  const  D3DSURFACE_DESC *  pBackBufferSurfaceDesc,  void *  pUserContext )
{
 
return  S_OK;
}

// --------------------------------------------------------------------------------------
//  在Direct3D设备进入lost状态后在IDirect3DDevice9::Reset 调用之前调用此函数,在OnResetDevice 
//  中创建的资源必须在这里释放,通常包括所有的D3DPOOL_DEFAULT 资源,
// --------------------------------------------------------------------------------------
void  CALLBACK OnLostDevice(  void *  pUserContext )
{

}

// -------------------------------------------------------------------------------------- 
// 这个回调函数在每帧的开始被调用,这个在你的程序中用来处理场景更新最好的位置,但不能包含实际的
// 场景渲染调用,渲染工作应该放在OnFrameRender 回调函数中。常用于矩阵转换、摄像机等操作。
// --------------------------------------------------------------------------------------
void  CALLBACK OnFrameMove( IDirect3DDevice9 *  pd3dDevice,  double  fTime,  float  fElapsedTime,  void *  pUserContext )
{
}

// --------------------------------------------------------------------------------------
// 此回调函数在每Frame最后被调用,在场景上执行所有的渲染调用,当窗口需要重绘(处理WM_PAINT消
// 息)时此函数也会被调用(此时不调用OnFrameMove),在此函数返回后,DXUT将调用
// IDirect3DDevice9::Present 来显示翻转链中下一个缓冲区内容。
// --------------------------------------------------------------------------------------
void  CALLBACK OnFrameRender( IDirect3DDevice9 *  pd3dDevice,  double  fTime,  float  fElapsedTime,  void *  pUserContext )
{
 HRESULT hr;
 V( pd3dDevice
-> Clear( 0 , NULL, D3DCLEAR_TARGET  |  D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0 0 0 0 ),  1.0f 0 ) );
 
if (SUCCEEDED(pd3dDevice -> BeginScene()))
 
{
  
// 更新图像
  pd3dDevice -> EndScene();
 }

}

// -------------------------------------------------------------------------------------- 
// 此回调函数在direct3d设备被销毁时调用,通常发生在程序终止,在OnCreateDevice 中创建的资源,要
// 在这里释放,通常包含所有的D3DPOOL_MANAGED资源
// IDirect3DDevice9::Present 来显示翻转链中下一个缓冲区内容。
// --------------------------------------------------------------------------------------
void  CALLBACK OnDestroyDevice(  void *  pUserContext )
{
 
}

INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, 
int  )
{
 
//  设置回调函数,这些函数允许DXUT通知应用程序更换设备,用户输入和窗口消息。
 
//  回调函数是可选的,因此你要做的仅是设置你感兴趣的事件的回调函数。
 DXUTSetCallbackDeviceCreated( OnCreateDevice );
 DXUTSetCallbackDeviceReset( OnResetDevice );
 DXUTSetCallbackDeviceLost( OnLostDevice );
 DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
 DXUTSetCallbackFrameRender( OnFrameRender );
 DXUTSetCallbackFrameMove( OnFrameMove );

 
//  初始化DXUT并创建想要的Win32窗口和应用程序的Direct3D设备。调用这些
 
//  可选函数中的每一个,此外它们允许你设置几个选项来控制框架的行为。
 DXUTInit( TRUE, TRUE, TRUE );
 
// directx 编码是unicode环境,所以字符串之前要加 L 。
 DXUTCreateWindow( L " Welcome to topameng.spaces.live.com "  );
 DXUTCreateDevice( D3DADAPTER_DEFAULT, TRUE, 
640 480  );
 
//  通过DXUT来处理消息循环并分派渲染调用。当在空闲时间和处理窗口消息的
 
//  时间间隔时,框架将调用OnFrameMove和OnFrameRender回调函数。
 DXUTMainLoop();
 
return  DXUTGetExitCode();
}

 

direct3d Memory Pools(内存池)
表面和其它一些D3D资源被放在多种内存池中。内存池的种类由D3DPOOL枚举类型的一个成员来指定。它可以为下列几种:
1.D3DPOOL_DEFAULT——表示D3D将根据资源的类型和用途把它们放在最合适的地方。这有可能是显存、AGP内存或者系统内存中。值得注意的是,这种内存池中的资源必须要在IDirect3DDevice9::Reset被调用之前消毁掉,然后必须重新初始化。
2.D3DPOOL_MANAGED——资源将由D3D管理并且按设备的需要来指定放在显存还是放在AGP内存中,同时备份这些资源到系统内存中。当应用程序访问和改变资源,也会对系统内存造成影响。
3.D3DPOOL_SYSTEMMEM——指定资源放在系统内存中。
4.D3DPOOL_SCRATCH——规定资源放在系统内存中,它与D3DPOOL_SYSTEMMEM不同之处在于使用这个参数使图形设备不能访问本内存池的资源,但资源可以被复制出去。
也可以打开direct sdk 安装目录Samples\C++\Direct3D\EmptyProject  例子,基本和上面是相同的