Loaden 个人专栏

自学编程!
posts - 0, comments - 0, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
re: Git Stash用法[未登录] Loaden 2011-12-26 09:53
stash是一个很有用的命令!
不会用stash,说明不会用git!
re: C++界面库的抉择[未登录] Loaden 2010-08-26 13:00
Qt并不适合个人开发小软件的!
Qt的最大缺点是LGPL无法静态链接。
这样,你只能背着好几十MB的DLL,这样的程序发布出去,打包也要接近10MB。
我想,这是无法让人接受的!
re: C++界面库的抉择[未登录] Loaden 2010-08-26 12:57
@陈梓瀚(vczh)
Code::Blocks就是跨平台的,使用wxWidgets库开发。
是你不会用罢了!
什么将你的界面库加进去呢?
你的界面库写的很漂亮啊!
re: wxWidget实现贴图[未登录] Loaden 2010-02-13 03:28
很漂亮,要是放个完善后的wxSkin出来就更好了。
或者放个Demo出来参考下。
Qt真漂亮!强大,学习来了.
x64下的thunk代码还是有问题。
普通thunk,非wndprocthunk。
期待与你联系。

我的QQ:1090833
下午还有课!虽然是星期天!!
唉。
这么好的文章,竟然只能晚上才能拜读,遗憾啊!
多谢cexer分享,期待下文...
typeid.name()的问题,通过CSDN解决了。
帖一下Demo。

struct Create
{
bool handled;
template<class T>
static int GetEventId()
{
return T::IdCreate;
};
};

struct Close
{
bool handled;
template<typename T>
static int GetEventId()
{
return T::IdClose;
};
};

class A
{
public:
enum
{
IdCreate,
IdClose,
TotalEvent
};

template <typename T>
void Get()
{
printf("%d\n", T::GetEventId<A>());
}
};

int main()
{
A a;
a.Get<Create>();
a.Get<Close>();
return 0;
}
@ OwnWaterloo
现在在理论上还可以有一种办法将:
m_testBtn.Get<EvtLButtonDown>(m_testBtn.IdLButtonDown).Bind(this, &MainFrm::OnLBtnClicked);
Get<EvtCreate>(IdCreate).Bind(this, &MainFrm::OnCreate);
简化为:
m_testBtn.Get<EvtLButtonDown>().Bind(this, &MainFrm::OnLBtnClicked);
Get<EvtCreate>().Bind(this, &MainFrm::OnCreate);

即不传入enum的id,改由Get函数根据typeid(T).name去判断id的值。
但如何去实现呢?

struct Create
{
bool handled;
};

class A
{
public:
enum
{
IdCreate,
TotalEvent
};

template <typename T>
void Get()
{
// 这条语句输出:struct Create
printf("%s", typeid(T).name());
// 可是,如何通过上面语句的输出,来访问: IdCreate呢?
// 从“struct Create中获取Create,在前面再加上Id,可以得到字符串IdCreate
// 如何将这个字符串转化为枚举型变量的一个值,例如:IdCreate?
//
// 这两行语句如何实现?
// int i = IdCreate;
// printf("%d", i);
}
};

int main()
{
A a;
a.Get<Create>();
return 0;
}

一种比较容易想到的方案是:
if (name=="A") id = ;
else if (name=="B") id = ;
但如果事件很多,这样去比较,代码写起来很麻烦。
而且编译后的程序体积也是个问题。

放到一个const字符串数组里,根据index判断,由于const数组是占用内存的(VC占用,测试过,GCC不占用),所以这样反而得不偿失了。
@ OwnWaterloo
谢谢!
我目前的方案,实现了每个可以发送消息的类,例如Button、Frame,至少需要8byte的代价,但:

50个事件供Bind:至少12字节
100个事件供Bind:至少20字节
1000个事件供Bind:至少132字节
10000个事件供Bind:至少1256字节
而我之前每个事件用一个指针的方法,分别对应:
200
400
4000
40000
代价下降了20倍。

不过每个注册的事件,还要多一个short + void*。
由于用户在一个程序中处理的事件不会很多,估计是总事件1/1000左右。
所以也是很值得的。

就算是一种低代价的实现吧。
相应类的消息注册类
class ButtonEvt
{
public:
enum EventId
{
IdLButtonDown,
TotalEvent
};

IMPLEMENT_EVENT(EventId, TotalEvent);
};

class FrameEvt : public EvtSender
{
protected:
FrameEvt(const String& keyEvt) : EvtSender(keyEvt) {}

public:
enum EventId
{
IdCreate,
TotalEvent
};

IMPLEMENT_EVENT(EventId, TotalEvent);
};

这样,只需要往enum里放不占内存的消息ID就行了。
@ OwnWaterloo
你用的是和atl一样的方式?
将hwnd"替换"为this? 而非"插入"一个this?
是吗?
====================
是的。因为这样才能兼容x64平台:x64前8个参数通过寄存器传递。

@ cexer
SetProp 结合 GUID更理想,而GUID可以用API获取。
以后多交流!期待你的GUI框架下一篇...^_^

================
|||||||||||||||||||||

发现一个类中有6万个指针时,如果指针初始化了,还是占用内存。
没办法,为了降低成本,不能在类中放那么多指针了。
所以,使用bitset来帮忙。

先看消息注册(比原来的难看多了,没找到好方法,详见:http://topic.csdn.net/u/20091119/15/07d7e755-c733-4195-8cc4-306560d6fbc4.html

class MainFrm : public Frame
{
public:
MainFrm() : Frame(_T("Test Demo!"), _T("MainFrame")), m_testBtn(_T("Test!"))
{
Add(&m_testBtn);
m_testBtn.Get<EvtLButtonDown>(m_testBtn.IdLButtonDown).Bind(this, &MainFrm::OnLBtnClicked);
Get<EvtCreate>(IdCreate).Bind(this, &MainFrm::OnCreate);
}

~MainFrm()
{
Get<EvtCreate>(IdCreate).UnBind(this, &MainFrm::OnCreate);
}

qpEvt OnCreate(EvtCreate& evt)
{
qpDbgInt(evt.handled);
}

qpEvt OnLBtnClicked(EvtLButtonDown& evt)
{
qpDbgInt(evt.owner);
}

protected:
Button m_testBtn;
};

用bitset + map<short, void*> + enum来降低成本:
#define IMPLEMENT_EVENT(X, Y) \
public: \
template <typename T> \
Event<T>& Get(X id) \
{ \
if (m_evtMap.get() == NULL) \
{ \
qpNewPtrEx(p, EventMap); \
m_evtMap.reset(p); \
} \
if (m_evtFlag[id] == 0) \
{ \
qpNewPtrEx(p, Event<T>); \
if (p != NULL) \
{ \
m_evtFlag[id] = 1; \
m_evtMap->insert(std::make_pair(static_cast<short>(id), p)); \
return *static_cast<Event<T>*>(p); \
} \
} \
else \
{ \
EventMap::iterator it = m_evtMap->find(static_cast<short>(id)); \
if (it != m_evtMap->end()) return *static_cast<Event<T>*>(it->second); \
} \
qpASSERT(false); \
qpNewPtrEx(p, Event<T>); \
return *std::auto_ptr<Event<T>>(p); \
} \
protected: \
typedef std::map<short, void*> EventMap; \
std::auto_ptr<EventMap> m_evtMap; \
std::bitset<Y> m_evtFlag;

std::bitset的使用,可以判断相应消息是否注册,不用每一个消息都进入一次Event了,但由于使用map来查找消息,效率上还是下降了。
没办法:只能用时间换空间!
@ vczh
我的网名叫老邓(非罗登^_^),曾经在Live上向你请教过GUI框架设计。

@ OwnWaterloo


evt::Create evt = {wnd, this, reinterpret_cast<LPCREATESTRUCT>(lpa), false}; \
...
this是怎么得到的?


这是一个宏替换。宏所在的类的this就是这里的this。


LRESULT CALLBACK Frame::WndProc(UINT msg, WPARAM wpa, LPARAM lpa)
{
...
估计也是非静态的吧? 因为有m_wnd这些东西。
那么,this是怎么得到的??

非静态。但只有三个参数,第一个参数通过汇编,写到了m_wnd成员变量上了。没办法:兼容x64平台,只能这样thunk。

另,昨天你教了我一下午,晚上我受到了启发,想到了一个利用bitset的方法来降低成本。
由于是动态注册,所以运行成本增加了。

f.Get<EvtCreate>(Frame::EvtCreate)->Bind(this, &A::test);

而且,看起来不舒服。
所以,今天准备结合你的union设计,把Get模板函数搞成属性来访问。
如果Demo通过的话,我发gmail给你。
@ OwnWaterloo
纠正一下,是随手 三 测 ^)^
第三次改输出代码时崩溃的。
@ OwnWaterloo

肯定要调用的……
不然怎么处理消息?
-----

消息处理在这个宏里。
写宏的原因是:不同消息处理类,可以通用。

// WM_CREATE
#define EVTCREATE(wnd, wpa, lpa) \
case WM_CREATE: \
{ \
evt::Create evt = {wnd, this, reinterpret_cast<LPCREATESTRUCT>(lpa), false}; \
EvtCreate(evt); \
} \
return 0;
struct Create
{
HWND wnd;
Object* owner;
LPCREATESTRUCT createStruct;
bool handled;
};
因为编译器知道这里肯定调用的是handle,不会是其他函数。
但thunk,调用的地址是运行时的,所以编译器无法优化。
-----
thunk不用调用,直接处理消息后,调用默认窗口过程。
比如:
LRESULT CALLBACK Frame::WndProc(UINT msg, WPARAM wpa, LPARAM lpa)
{
LRESULT res = 0;
bool ret = ProcessMessage(msg, wpa, lpa, res);
if (ret) return res;

if (msg == WM_SIZE && m_layout != NULL)
{
m_layout->DoLayout(0.0f, 0.0f, static_cast<float>(X_LPARAM(lpa)),
static_cast<float>(Y_LPARAM(lpa)), true);
}

switch (msg)
{
EVTCREATE(m_wnd, wpa, lpa);
}

return ::CallWindowProc(m_defWndProc, m_wnd, msg, wpa, lpa);
}

static函数...
-----
可能是个人喜好吧。
我喜欢尽最大可能的 不 在C++中使用全局函数和静态函数。
我喜欢将什么东西都放在类里。
哪怕是无法实例化的静态类。

我对你在前面给的“低代价”例子非常感兴趣,下午准备好好学习你的例子。
谢谢!!
@ OwnWaterloo
CWindow* w = (CWindow*)GetWindowLongPtr( ... );
这个调用不仅仅是返回一个值而已。
而且不同的窗口类,要返回不同的值。
所以,这里编译器如何优化呢?
另外,static函数、全局函数也不利于C++封装。
而thunk,从其汇编代码可以看到,代价只是多花1~3条CPU指令而已。
显然要高效。

DEP的问题,我想过内存池来实现。
不过由于技术不过硬,暂时还没想过去实现它。

先把GUI框架稳定下来再说。
谢谢你的指点!如果有好的解决thunk的内存池(ATL就是这么干的,只是不开源),请介绍一个给我。
@ OwnWaterloo
因为要兼容x64平台,所以统一使用__stdcall。
原因是x64下只有__stdcall。

DEP不是问题,很容易解决:
void* operator new(size_t size)
{
return ::VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}

void operator delete(void* p)
{
::VirtualFree(p, 0, MEM_RELEASE);
}

而:
Set/GetWindowLongPtr
还有前几天学到的…… Set/GetProp
由于使用static函数,效率是一个大问题,而且有潜在的危险性。

谢谢你分享:callback-currying
其中qpEvt的定义:void __stdcall
因为使用thunk,所以回调函数必须是__stdcall的调用约定。
另,任何一个类如果要处理消息,需要这样继承:
class Test : public EvtReceiver<Test>
{
public:
Test();
~Test();

void UnBind();

private:
qpEvt OnCreate(evt::Create& evt);
};

然后这样注册:
Test::Test()
{
FrameEvt *sender = App::Module().GetEvtSender<FrameEvt>(_T("MainFrame"));
if (sender != NULL)
{
sender->EvtCreate.Bind(this, &Test::OnCreate);
}
}

Test::~Test()
{
}

void Test::UnBind()
{
FrameEvt *sender = App::Module().GetEvtSender<FrameEvt>(_T("MainFrame"));
if (sender != NULL)
{
sender->EvtCreate.UnBind(this, &Test::OnCreate);
}
}

qpEvt Test::OnCreate(evt::Create& evt)
{
qpDbgInt(evt.handled);
}

但其中:
FrameEvt *sender = App::Module().GetEvtSender<FrameEvt>(_T("MainFrame"));
是极度龌龊的!可是我找不到更多的办法了。
@ OwnWaterloo
能否QQ、gmail、Live联系?有问题请教。
我的QQ:1090833
我的GMail、Live:loaden AT gmail or live dot com
之前在gmail是向vczh请教时,受益匪浅。
想结识牛人:不会啰嗦,只想在关键的疑惑上得到点拨。

@ cexer
非常期待你的GUI设计。
我现在倾向于使用thunk封装,但由于还碰到很多问题,没办法拿出来讨论。
非常期待能与你在QQ or GMail or Live上探讨GUI框架设计。
我目前实现的消息映射是这个样子:
[code]class MainFrm : public Frame
{
public:
MainFrm() : Frame(_T("Test Demo!"), _T("MainFrame")), m_testBtn(_T("Test!"))
{
Add(&m_testBtn);
m_testBtn.EvtLButtonDown.Bind(this, &MainFrm::OnLBtnClicked);
this->EvtCreate.Bind(this, &MainFrm::OnCreate);
}

~MainFrm()
{
this->EvtCreate.UnBind(this, &MainFrm::OnCreate);
}

qpEvt OnCreate(evt::Create& evt)
{
evt.handled = true;
qpDbgInt(evt.handled);
}

qpEvt OnLBtnClicked(evt::LButtonDown& evt)
{
qpDbgInt(evt.owner);
}

protected:
Button m_testBtn;
};[/code]
@ OwnWaterloo
@ vczh
(注:排名不分先后,按字母顺序)

你们两个都是超级牛人!!我看你们的争论时,更多的是自卑感!
因为你们讨论的很多东西我看不懂!!
唉!!
我把郁闷发在这里了,有空就过去给我签个名吧。
http://topic.csdn.net/u/20091117/19/54a04541-094f-4d7c-960e-c0ce34783821.html

两位不要伤了和气!技术问题,有争论总是好的!!
我就在旁边学习好了...
强烈猛顶!
  写到这,我作为轮子制造爱好者,在这里向那些喊着"不要重复制造轮子的"批评家们承认错误。在有那么多好的轮子的情况下,我不值得浪费地球资源,浪费时间精力来自己手工重复打造。但是不值得归不值得,在值得和喜欢之间我还是选择后者。并且人生在世,什么才是值得?我觉得不是拯救人类,为世界和平做贡献,也不是努力奋斗,为地球人民谋福利,而是简单地做自己喜欢的事。

看来我和楼主是一样类型。
我也是从封装类似ATL框架开发,目前正在模仿jlib2...
还有很长的路要走!
向楼主学习!
我认为你的方法如果不是在基类中已经定义,那是无法实现的!
能否帖出核心代码?
消息处理的很好!
可是,new之后没有delete?
希望可以增加Code::Blocks支持,不知道项目难度如何,以及加入该项目有何要求?我原来从事过WTL开发。
非常棒!!
re: FLTK简介[未登录] Loaden 2009-05-26 20:24
2.x对中文支持很好的。
源码使用UTF-8,GCC编译器,中文显示正常,输入也正常。
一个非常奇怪的问题:我编译binutils成功,但make check总失败。
不知为何。
博主你好!我只想编译c/c++,使用make 及make install应该不行。
于是我使用:
make all-gcc all-target-libstdc++-v3 all-target-libssp all-target-libgcc && make install-gcc install-target-libstdc++-v3 install-target-libssp install-target-libgcc

上面是通过configure之后的makefile文件自己判断的。
虽然我能编译成功,但总出现crt2.o找不到的问题。

另,我已经编译MinGW好几天了,有一些体会,但也有不少疑惑,想与博主QQ交流:1090833,能否加我QQ聊?
mpfr也编译成功了,正准备编译gcc
我编译GMP4.3成功了,不过编译mpfr出现错误。
从VC8开发,VC的lib变了,不能再给MinGW用了。
反过来也一样。

TDM版本还是不错的。

我正在尝试编译gcc4.4!
mov qword ptr [rax], rcx
这一行的机器码不对,要改成:488908
另,我在x64下用WinDBG调试发现:mov rcx, rax 的机器码应该是:488bc8
但如果用:4889c1 也可以。但我认为还是应该改成:488bc8

不知道你看过ATL的atlstdcall.h没有?如果能在他的基础上实现thunk,可能更加完善。毕竟要修改第一个数据成员:这还是有局限性了。Thunk的功能打折扣了。

但x64的难题是:前四个参数都在寄存器中。
我目前的思路是:把前四个参数依次后移,将第四个参数入栈,可我只会点汇编的皮毛,能否指点一下如何实现?只要写出汇编代码即可,机器码我可以通过WinDBG来调试得到。
我在CSDN发了个求助帖,能否帮忙看看。
http://topic.csdn.net/u/20090322/08/b6bf82ca-8ba2-452b-92f8-bb2adb05a1ef.html
因为我汇编外行,所以只能尝试修改。而我是在qemu虚拟x64机下测试,很慢。
我觉得理论上完全行得通,但是否x64的汇编机器码对应有误?能否帮忙检查一下。
非常好的思路,可以实现跨x86和x64
在x86下测试通过,但在x64下失败:崩溃了。
能否加我QQ:1090833,或我加你QQ,想请教一下崩溃的原因以及如何修正。
有严重缺陷:如果用户原来是管理员,后从管理员组中删除,则上述代码仍然认为是管理员。
很好,非常感谢。可是无法下载了。如果可以,请发份一给:loaden AT gmail.com