CEGUI,复制粘贴—— lua,UTF8

十二月 27th, 2010 266 次阅读 0 条评论

 

让CEGUI的Editbox控件支持复制,粘贴。

 

鉴于CEGUI内部,集成太多的东西,不利于维护,因此对windows Clipboard 的API函数的控制,放在客户端实现,同时CEGUI只负责Ctrl+C,Ctrl+V被按下的时候,发出相应的消息即可。

 

但这样就又引出了新的问题:负责处理该事件的回调函数在lua中,对utf8编码的处理比较烦,关键是没有相应的字符串函数供你调用;回想一下,C++中的insert(),erase(),length()用的这么舒心,你该感概库的强大作用了吧! 

 

没关系,自己动手丰衣足食嘛!首先搞懂UTF8的编码规则: 

1. 字符的第一个字节范围: 0x00—0x7F(0-127),或者 0xC2—0xF4(194-244);

2. 0xC0, 0xC1,0xF5—0xFF(192, 193 和 245-255)不会出现在UTF8编码中 

3. 0x80—0xBF(128-191)只会出现在第二个及随后的编码中(针对多字节编码,如汉字) 

 

 这样我们可以利用lua强大的模式匹配,来实现我们要的效果,关键的处理有这么两个: 

1. local _, count = string.gsub(str, "[^\128-\193]", ""),用来得到str中的字符数 

2. for uchar in string.gfind(str, "[%z\1-\127\194-\244][\128-\191]*") do tab[#tab+1] = uchar end,用来把str中的每个字符映射到tab中

 

另外附上windows Clipboard的相关API:

 

if(OpenClipboard(0))
{

        EmptyClipboard();
        HGLOBAL clipbuffer = GlobalAlloc(GMEM_DDESHARE, info.length()+1);

        char *buffer = (char*)GlobalLock(clipbuffer);

        strcpy(buffer, info.c_str());

        GlobalUnlock(clipbuffer);

        SetClipboardData(CF_TEXT,clipbuffer);

        CloseClipboard();

}
//--------------------------------------------------------
if (!IsClipboardFormatAvailable(CF_TEXT) || !OpenClipboard(0))
{
        reutrn false;
}
HGLOBAL hMem = GetClipboardData(CF_TEXT);
if (hMem != NULL)
{
        LPTSTR lpStr = (LPTSTR)GlobalLock(hMem);
        if (lpStr != NULL)
        {
               content = std::string((char*)lpStr);
               GlobalUnlock(hMem);
        }
         else content = "";
}
else content = "";
CloseClipboard();

 

对付一般的应用,这样做足够了;如果高手路过,能有更好的解决方案,欢迎赐教!

分类: 技术 标签: cegui 

Release Debug 初始化

十一月 2nd, 2010 115 次阅读 0 条评论

这两天碰到了一个bug,表现是这样的:Debug下一切正常,一派祥和之气;Release下暗潮汹涌,很不正常;Release模式编出来的东西,用lua的Decoda调试也乖得很,不出任何问题。这是怎么回事呢?


扫一遍代码,没看出端倪,再扫N遍,还是没有收获;最终还是通过调试找到了问题出在哪里,并解决掉。回头看看,也算是费了很大的劲才搞定。


首先说下问题出在哪里:类中的一个数据成员没有被初始化;其次,说下解决方法:在构造函数的初始化列表中正常初始化。一切就这么简单。


发现问题,解决问题是一方面,而完全搞懂问题,掌握问题则是另一方面。不完全弄清楚前因后果,谁能保证下次你不会犯同样的错误呢?简单解释下原因:

你通过new创建了一个对象,然后销毁,再然后用new创建了一个完全一样的对象。假如这个对象的类中有一个数据成员,既没有被初始化,也没有在析构函数中进行类似清0,复位之类的操作,并且如果你之后new出的对象是建立在原来的内存空间上,那么此刻该成员的值就是你最初创建的对象销毁之前对应的值。更进一步,该值如果在你的程序中占据着很重要的位置,那难免不会出错。

总结看来,release进行了优化,第二次new出的对象,是建立在原对象的内存空间上;而debug和调试模式下,第二次使用的内存空间和第一次的 位置上没必然联系(这个地方说的不够严谨,因为我现在也不清楚这方面的具体细节。望高手指点,有空详细补充上)

说起来总是很抽象,可以看这样一个例子:

class Control
{
	void destroyWorld(){..}
	void happy() {..}
public:
	int year;
	Control(){}
}

Control *ptr1 = new Control;
..
ptr1->year = 2012;
..
delete ptr1;

Control *ptr2 = new Control;
..
if((ptr2->year) == 2012)
	ptr2->destroyWorld();
else
	ptr->happy();

举例而已,我们不会有这样简单的失误;

另一方面,实际情况要“含蓄”很多很多;

代码不是人,绝不模棱两可,0是0,1是1;

程序员绝对必须要有清晰透彻明朗的感觉;

 

没看懂上面所有的东西也没关系,但请务必记住这样一句话:养成良好的习惯,对象的所有数据成员,必须进行初始化!

分类: 技术 标签:  

概率 原则

十一月 1st, 2010 108 次阅读 0 条评论

假设你正在参加一个游戏节目,你被要求在三扇门中选择一扇。其中一扇后面有一辆车,其余两扇后面则是山羊。你选择了一扇门,假设是1号门,然后知道门后面有什么的主持人开启了另一扇后面有山羊的门,假设是3号门。他然后问你:“你想选择2号门吗?”那么,改变你的选择对你来说是一种优势吗?

电影《21点》中也出现了这样的论题,那个数字天才的选择是绝对要改变,并且给出了这样的解释:第一次选择时,会出现两种可能性:选到了山羊,或是选到了汽车。因为有两扇门背后都是山羊,所以参赛者选到山羊的概率是2/3;相应地,选到汽车的概率是1/3。主持人打开了一扇山羊的门后,我们假设参赛者决定更改选择。那么,假如参赛者一开始选的是山羊(2/3的可能性),那么他就会换到汽车;假如参赛者一开始选的是汽车(1/3的可能性),他就会换到山羊。这也就是说,参赛者更改自己的选择便会有2/3的概率获得汽车;与之对应的不改变策略,则获得汽车的概率就只有1/3。

还有一个更典型的例子——假设有52张牌,只有一张是King,选中King则获得大奖。假设你选择了一张牌,然后奖励方将剩下的51张牌中的50张全部翻开,没有一张是King。然后告诉你,你可以选择换或者不换。那么你到底应该换吗?比照上面,答案是换。这里可以换一种解释的方法:你手中拿的牌显然只有1/52的概率是King,剩下的所有牌有51/52的概率包含King,既然翻开了50张不是King的牌,那么只剩下的另一张牌是King的概率就大的吓人,高达51/52。自然要换。

好,相信很多人看到这样的解释会认为合情合理,但是如果我们再换一个角度呢?去繁就简,管他之前发生过什么呢!现在我们只知道自己手头有两张牌,只有其中一张是King,我们要在两张牌中做一个选择。这完全可以是一种典型的抛硬币问题。设身处地想一下那个场景,难道不是吗?更进一步,我们可以引入其他因素来左右我们的选择:运气很重要,我相信我之前的选择;哦,我有强烈的预感,不换会赢;之前我运气不好,我还是选择换一下吧。。。此时此刻,又有多少同学会认为这样的解释也是合情合理呢?起码一直以来我认为以这种角度看待这个问题也完全是可以的,所以我一直很难理解概率的指导意义。而且很讽刺的是学术界支持上一种选择的同时也支持这种选择,因此很多人认为这样的解释是合情合理的,其实很合情合理~~

相信学术界支持第一种选择,但不相信学术界支持第二种选择??那么请欣赏他们发明的赌徒谬论:赌徒谬论是指倾向于相信未来(某事发生)的几率将会由过去的事件所改变,但实际上那是扯淡。必然概率——如你投掷一枚(正常的)硬币正面朝上的概率——是不会改变的。正面朝上的概率永远是50%,即使你已经连续投出了十次背面朝上(下次投出正面的概率依旧还是50%,不会增大或缩小)。认为概率会改变是很多人都有的认知偏误,尤其是在赌博的时候。举例来说,我玩轮盘赌博,已经连续四次转停在黑色区了,下次总该是转到红色区了吧?错!转到红色区的几率仍然是47.37%(总oooooo共38个点区,红色有18个)。这听上去似乎显而易见,但正是这种几率可变的潜意识让赌徒们输的得血本无归。

两个绝对矛盾的事情都合情合理,这显然不合情合理,那问题究竟出在哪呢?后来我逐渐想明白了,概率的指导意义其实与样本空间呈正比关系。样本越大,则概率越神奇;样本越小,则概率可以忽略不计,我们完全可以赌一下自己的运气,谁说结果不会是更好呢?回到之前的具体例子,也就是说,假如给我1000次进行竞猜的机会,当面临上面那种情况,我绝对会改变自己之前的选择,但是假如只有一次这样的机会,管他呢,我宁愿相信自己当时的直觉,或者干脆抛硬币。 生活中会碰到很多与上面类似的情况,当然对于同一件事我们不可能同时有那么多次机会来做出不同的选择;那么我们究竟应该“反正只有一次的机会,管他呢,抛硬币去也”还是“运气这东西更玄乎,我还是要依照概率来做选择”?

----------------------------------------------------

所谓原则,是这样一个东西: 很多时候你搞不懂为什么要这么做,并且这样做在特定的某个时间,事件中也完全有可能是错误的;但另一方面,从长远来看,从全局来看,你每次都按照原则行事,却可以保证你利益最大化。概率的指导意义正是如此!

因此,我相信概率,并坚持原则。

分类: 心理 标签:  

CEGUI很难响应双击事件的解决方法

十一月 1st, 2010 245 次阅读 0 条评论

项目中,某一天,突然出现了这样的问题:CEGUI中的控件很难响应到双击事件。之前双击一下房间列表中的图片很容易就进到某一房间了,而现在要在控间上连续猛击N下才侥幸进的去。

昨天,好好的研究了一下这个问题,最后终于解决掉。

首先,cegui内部支持两种方式产生鼠标双击事件:

第一种,接受window直接发送的双击事件,与之对应,外部要有一个消息注入:

 

case WM_LBUTTONDBLCLK:

  CEGUI::System::getSingleton().injectMouseButtonDoubleClick(CEGUI::LeftButton);

  break;

 

第二种,连续接收到两次鼠标单击的事件,如果间隔事件小于某一临界值,则被cegui整合为一个双击事件;

--------------

其次,windows中有一个WNDCLASSEX,它的style成员的值决定了会不会产生双击消息;

 

如果出现了wndClassex.style = CS_DBLCLKS这样的形式,则间隔时间够短的两次鼠标单击事件会产生一次单击消息和一次双击消息(也就是说此时第二次单击被判断为了一次双击);

如果wndClassex.style = 0(或其他值),则windows不会发送出双击消息,再快的两次单击事件也只能够产生两次单击消息;

 

回到我们的问题,之前,客户端的代码是这样的,wndClassex.style = 0,因此两次很快的单击事件被注入到cegui内部时,会被cegui判断成一次双击事件,正常触发相应的双击事件处理函数;(对应着CEGUI上面第二种产生双击事件的方式)

 

而现在,客户端的代码是这样的,wndClassex.style = CS_DBLCLKS,也就是说很快的两次单击事件,产生了一个单击消息和一个双击消息,这当然也没有问题,问题是,客户端那边竟然搞忘了将双击消息注入到cegui内部,即缺少了上面贴出的那段代码;最终导致cegui既不能以第一种方式产生双击事件,也不能以第二种方式产生双击事件;(此时当然应该是以第一种方式产生双击事件)

 

一开始以为是cegui自己的bug,看了人家的代码之后发现没有问题,才从客户端入手。。看似简单的问题调试了多半天的时间

 

 

分类: 技术 标签: cegui 

周末杂感

五月 23rd, 2010 182 次阅读 0 条评论

好久没看到这么精彩的电影了。我是指《My name is khan》。有关爱和信仰。我们可以没有雄鹰展翅翱翔的实力,但是我们必须有蜗牛一往直前的毅力。一直在寻找窗明几净的内心世界!

昨晚欧冠国米一个干净利落的2:0夺得冠军奖杯,即便我心里是支持拜仁的,但仍为穆里尼奥举双手喝彩;当然还有迭戈.米利托,很难想象两年前这位大器晚成的阿根廷人还在为萨拉戈萨的保级而战,如今却在最顶级的赛场上沉稳犀利地独中两元,最终登上欧洲之巅。

夏天终于来了,离别的气息开始显现。。。。。。。。。。。。。。。

最后附上:《My name is khan》里面的一句经典台词,:The theory of entrainment in Physics states that some specific sounds increase the rate of your heartbeat. for me,mandira,that specific sound has always been the sound of your laugher。

 

分类: 生活

《C++编程思想》学习笔记——拷贝构造函数(1)

五月 21st, 2010 202 次阅读 0 条评论

//A classs that counts its objects

#include<fstream>
#include<string>
using namespace std;

ofstream out("HowMany.out");

class HowMany {
	static int objectCount;
	public:
	HowMany() { objectCount++; }
	static void print(const string& msg = "") {
		if(msg.size() != 0) out << msg << ": ";
		out<<"objectCount = " << objectCount <<endl;
	}
	~HowMany() {
		objectCount--;
		print("~HowMany()");
	}
};
int HowMany::objectCount = 0;

//pass and return BY Value:
HowMany f(HowMany x) {
	x.print("x argument inside f()");
	return x;
}

int main()
{
	HowMany h;
	HowMany::print("after construction of h");
	HowMany h2=f(h);
	HowMany::print("after call to f()");

	return 0;
}
输出的结果:
after construction of h: objectCount = 1
x argument inside f(): objectCount = 1
~HowMany(): objectCount = 0
after call to f(): objectCount = 0
~HowMany(): objectCount = -1
~HowMany(): objectCount = -2
可以看出没有拷贝构造函数之前,一个对象的赋值生成(通过=) 和 按值传递将会不调用构造函数,不调用构造函数意味着不能正确初始化,而没有正确初始化是很多问题产生的根源。
ps:上述程序参见《thinking in C++》第十一章,有关静态成员函数和静态成员变量的部分可以参考第十章。(简单的位拷贝在类中将会出现很多问题,这也是我们引入拷贝构造函数的原因)
分类: 技术

《C++编程思想》学习笔记——语法细节

五月 8th, 2010 216 次阅读 0 条评论

1.逗号运算符的值是最右端的表达式,如:d=(a++,b++,c++),是将c的值赋给d,同时a,b,c分别自增一。ps:这种写法不好!

2.指向函数的指针变量一般定义为: 数据类型 (*指针变量名)(函数参数表列)。P100,一些有关函数指针的复杂声明,保证让你看的很崩溃,比如最简单的一个:void * (*(*fp1))(int)[10];fp1是一个指向函数的指针,该函数接受一个整形参数并返回一个指向含有10个void指针数组的指针。

3.指针数组:int *p[4]。p为一个数组,每个数组的元素为指向整形的指针。解释:由于[]比*优先级高,因此p先与[4]结合,形成p[4]形式,这是数组形式,他有4个元素,然后在与前面的*结合,*表明此数组是指针类型。

4.数组指针:int (*p)[4]。p为一个指针,指向一维数组,该数组大小为4.

5.如果void类型的指针指向一个对象的话就不能正确地将其删除。

6.初始化和清除是保证安全性的很重要的两个方面;很多程序难题来源于这两个方面。

7.通常传递到构造函数的第一个参数(秘密)是this指针,也就是说调用这一函数调用这一函数的地址。

8.构造函数和析构函数是两个非常特殊的函数。首先他们没有返回值,这与返回值为void的函数不同,后者还可以做点别的事情;其次,构造函数没有参数;设计成没有返回值的特殊形式是从安全角度考虑。

9.当一个类中出现嵌套的类(或struct),要特别主要分别设计好他们各自的析构函数,特别是涉及到指针操作时;这里面有一个所有权(ownership)问题,清楚一个对象应该由谁负责,应该考虑周全,否则很容易出现内存泄漏。

10.默认参数源于函数重载,出发点在于使函数调用书写起来简单。有两条原则:声明原则--只有参数表的后部分参数才是默认的(换种说法,不可以在一个默认参数后面又跟一个非默认的参数);调用原则--一旦在一个函数调用中开始使用默认参数,那么这个参数后面的所有参数必须是默认的(换种说法,如果f()有四个参数,那么调用f(1)1一定对应着前1个参数,f(1,2)1,2一定对应着前两个参数,剩下的所有的参数都是后面的且是默认的参数。

11.int* u=&i,v=0;这行程序的作用是,建立一个int*型的指针u,将j的地址赋给它,其次建立一个int型数据v,赋值为0;这里*号与标识符结合,而不是和类型int结合;当然,写成int *u=&i,v=0会好些,但绝不提倡这种写法;分成两行,int *u=&i; int v=0,清楚的多。 

 

分类:

C++中的引用

五月 8th, 2010 165 次阅读 0 条评论

引用就像能自动地被编译器间接引用的常量型指针。

引用的应用要点是:任何引用必须和存储单元联系。

思考引用的最简单的方法是把它当作一个奇特的指针。这个指针的一个优点是不必怀疑它是否被初始化了(编译器强迫它初始化),也不必知道怎样对它间接引用(由编译器做)。

使用引用的规则:

1.当引用被创建时,它必须被初始化(指针则可以在任何时候被初始化)。

2.一旦一个引用被初始化为指向一个对象,它就不能改变为另一个对象的引用(指针则可以在任何时候指向另一个对象)。

3.不可能有NULL引用,必须确保引用是和一块合法的存储单元关联。

C语言中要想改变指针本身的值,应该这样做:

void f(int**);

int i=47;

int *ip=&i;

f(&ip);

利用对于C++的指针引用,语法清晰多了:

void increment(int *& i) {i++;}

int main()

{

int *i=0;

cout<<"i= "<<i<<endl;

increment(i);

cout<<"i= "<<i<<endl;

}

运行这个程序将会发现指针本身增加了,而不是指向它的内容增加了。

分类: