posts - 16,  comments - 34,  trackbacks - 0
共10页: 1 2 3 4 5 6 7 8 9 Last 
re: GUI框架:消息检查者 OwnWaterloo 2009-11-23 02:21
@cexer
确实…… cppblog的评论做得不咋嘀……
所以代码我也没仔细看…… 意思差不多明白了。
WndProc可以用这些参数表达任意多的参数,所以我们也可以……
对不需要多余参数的,可以直接使用这几个。
对需要的,再引入多态与自由存储的开销,并将其包裹在一个值语意的类型中。
是这样吗?

这思路不错。 预留几个常用的参数,免得需要参数就要动用动态内存……



说实话我一直对“框架”和“类库”的概念分不大清楚,我想“框架”从名字上来说多了一个“可以扩展,可以填充”的意思,没有你说的“限制你做事的方法”这种感觉。

哎…… 因果弄反了……
为什么需要【扩展】框架? —— 就是因为框架限制了做事的方式,不扩展就没法做自己需要但框架又不提供的功能了。

说得好听点,叫扩展了框架;说得不好听,叫顺了框架的意;再难听点,就是被框架qj了……


以你这篇文章里提到框架来说吧:
1个listener 有 1个m_map
1个m_map 有若干个HanlderPair
1个HandlerPair 包含一个 Checker和HandlerVector
1个HandlerVector 包含若干个Handler

这就是一种限制。MessageChecker也算。

设:从源头WndProc到实现某个功能所需要做的全部事情为S。
【必须按这个框架制定的规则,将S拆分为S1、S2等等,然后安排到框架中的预定位置。】
比如将事情分为监听、检查、映射、处理等工作。

这就是我说的限制的意思。【框架规定了编程的模型】。

假设,有一项需求框架没有提供,比如Checker需要更多的参数。
如果还想继续使用这个框架,就必须设计一个MessageData。
假设MessageData用户而非框架提供的,是为【扩展】了框架。
假设MessageData是框架自身提供的,但如果它又有另一个需求没有满足呢? 它必须提供一些【可扩展点】让用户【大部分按框架的思路编程】,并在需要的时候,扩展一下框架。否则用户就会放弃这个框架了。

如果框架的规定在大部分时候合理,框架就是合理的。
这个尺度很难把握……

总之,用框架编程,框架是主体。或者说底层的实现不用用户操心,只需要注重业务逻辑。
用类库编程,程序员自己是主体。




倒是没考虑过使用函数来实现消息检查者,我更喜欢它有对象的感觉一点,OOP语言嘛,而且类是肯定比函数有大得多的灵活性的。

这个,其实也是反的……
OO是思想,class只是它的一种实现方式。也许使用会比较方便。
但灵活性是不如函数+数据的。

1.
比如C++、java、C#都没有提供多重分派(multi-method)。
所以,很明显的,o.method(...); 只是method( o , ... );在1个对象下的简便形式。
如果需要method( o1, o2, ... ) —— 根据o1和o2的类型来决定做什么事。这3门语言是不提供支持的,也必须用函数来表达。
我知道有个xxx模式…… 但3重分派它又解决不了了…… 治标不治本。

当然,某种支持多分派的语言可以继续提供 o1&o2.method( ... ); 的语法……

2. OO将算法埋葬到自己的类里。
如果这个算法很通用,更好的是将它实现为一个函数,而不是委身到一个class中。
呃,这种情况在gui中可能不多见。

re: Void and void pointer OwnWaterloo 2009-11-22 18:54
1.
C/C++大多数时候是可以混着说的,有时候不行,比如void*到T*的隐式转换。
int* pi;
void* pv;
pv = pi; /* C and C++ ok */
pi = pv; /* C ok, C++ error */


2.
如果一个函数:
1. 可以被C编译器编译
2. 参数列表为空

确实在参数列表中加上void是一个好习惯。可以作为一个规范。
但不是为了自注释与可读性。
自注释与可读性是瞎扯 —— 连这都不知道的C程序员就别写C代码射自己的脚。

对其他情况,加上void也不是自注释。
struct C
{

C(void);
/* 嘿,其实C()是C(void)的意思! */

static void operator new(size_t);
/* 嘿,其实这里加不加static它都只能是static! */

};
而是罗嗦。 连这都不知道的C++程序员更去别写C++代码炸自己的脚。


3. void* pv; ++pv;
GCC这个扩展很多时候还是很方便的……
但是,如果想将程序移植到非GCC上,应该克制使用这个扩展。
检测出这个扩展的方法是:
3.1 使用其他不支持这个扩展的编译器编译……
3.1 使用GCC编译,并使用-Wpointer-arith 或者 -Werror=pointer-arith选项


4.

void * pvoid;
(char *)pvoid++; //ANSI:正确;GNU:正确
(char *)pvoid += 1; //ANSI:错误;GNU:正确

这段代码…… 要一句话说清楚挺困难……
总的来说,遵守ANSI的编译器上,2行都是错的。
GCC上如果正确总是因为GCC的扩展。

4.1 (char *)pvoid++;
它的意思是 (char*)(pvoid++); 利用了GCC对void*进行算术操作的扩展。
所以在GCC上是正确的,在ANSI上是错误的。

4.2 (char *)pvoid += 1; 以及:
++(char*)pvoid;
((char *)pvoid)++; /* 使用括号改变优先级 */

在ANSI上也是错的。因为后缀前缀++,还有复合赋值的操作数必须是左值,而转型操作符只能得到右值。
GCC较低版本在此处又有扩展…… 可以在右值上执行上述操作,所以可以编译通过。
但这个扩展被标记为废弃,在GCC高版本中被移除。

re: GUI框架:消息检查者 OwnWaterloo 2009-11-22 15:06
将_HandlerMapIter改为HandlerMapIter或者HandlerMapIter_真的没有坏处。
本来就是一个private,库用户不可见的名字。
属于Handler,也不会和其他重名。
还避免了(可能微乎其微)与_HandlerMapIter宏冲突。
re: GUI框架:消息检查者 OwnWaterloo 2009-11-22 15:01
@cexer
好像没有,我是宅男……

boost有自己的苦衷。它需要一个简短的东西来做占位符。
这样写就太繁琐了:
bind( f, first, second , ... ) ();
而且first,second估计很容易和程序员冲突。
1、2又不是合法的标识符。 所以干脆搞成_1, _2,至少不会和程序员冲突了。
bind( f, _1, _2 ... )( ... ); 直观

而_HandlerMapIter改为HandlerMapIter或者HandlerMapIter_并不会影响什么,而且也符合与C/C++定下的契约。


boost将_1,_2改为1_,2_是不合法的,不能以数字开头。
改为其他,不太简洁……



也不能算完全的学究。它们不会永远被保留。
C99已经开始动用这些保留的标识符了。
_LongLong, _Complex,_Bool等。
C++也会跟进。

@唐风
后来又回味了一下,用SFINAE,故意让使用某些类型实例化模板时产生错误,从而从重载候选中剔除,确实是一个很好的办法~_~

boost的enable_if也确实抓住并满足了大量需求。

好东西啊,再回味回味...

re: GUI框架:消息检查者 OwnWaterloo 2009-11-22 14:38
@cexer
说说我对gui框架的需求吧……
以用户的身份…… 用户的需求总得听听吧……


从这里说起:
【目前没遇到id,wparam,lparam 三个参数解决不了问题的情况】
我编写gui的经验不多。所以对"这3个参数是否能解决所有问题"是一点概念都没有。

不过我想了一个例子,比如我要在xxx-yyy时间段内,检查这个消息,返回真,这个时间段外,就返回假。
这个checker就做不到了。 肯定要放到其他地方做,比如hanlder中。


对,这就是我对"框架"感到反感的地方之一 —— 它"限制"你做事的方法。
它会将可能本来是一件完整的工作,拆散,然后分散到框架的一些地方去。

为了完成这个工作,你要按框架所拆分的那样 —— 需要顺着框架的思路,而非我自己的思路 ——只需要id,wp,lp就能check的,放到checker中。其他即使也是check,也得放到handler中。
如果框架拆分得恰当,就很好,比如 CommandChecker等。
如果框架拆分得不恰当,就很糟……
所以我对mfc一点好感都没有。


作为一个了解且不排斥win32api的用户(当然,gui框架大多着眼的都不是这种用户囧……),我需要的仅仅是弥补一下WndProc,使得它可以找到this —— 这个工作几乎是必须做的,即使是用C编写gui的家伙。
然后,就可以从hwnd,msg,wp,lp开始干活了 —— 不需要再学任何关于框架的知识,比如要将check放在哪,hanlder放在哪。
我希望框架提供一个,在window上add(挂)多个listener的机制就差不多了…… 如何分配checker和handler还有cracker的工作,还是将它们揉在一起,用户可以自行决定。

所以我对lambda表达式特别渴望……
比如上面的代码,对应的伪代码可能是这样:

window w;
w.add_listener( void (const msg& m) { if (m.msg==WM_CREATE) cout<<"create\n"<<endl; } );


然后,在每天编程闲暇的时候,发现库(而非框架)里面有command_check,我就可以这样写代码了:
w.add_listener( void (const msg&m) { if (is_command(m,id) cout<<command_crack(m).id<<endl; } );

如果我没发现command_check,我也可以开始我的工作,只是代码更繁琐一些。

这就是库和框架的一个很大的区别。
你可以使用熟悉的方式工作,然后慢慢熟悉这个库,慢慢用它干善自己的工作,并提高效率。
而框架,不对它熟悉到一定程度,就没法工作……



作为程序员……
我也了解用户的需求是很恶心的…… 就像你有一篇blog里写的那样……
所以,推己及人……
对我提出的这个恶心的需求,听过之后就当是废话好了~_~

re: GUI框架:消息检查者 OwnWaterloo 2009-11-22 14:04
@cexer
刚醒…… 一边吃饭一边先把不涉及风格的东西解释一下……

不说不变式了。我也觉得这词太学究,所以在后面补了一句:
【调用的是子类的函数,使用的是父类的数据(被切割了)……】

而且,我说错了……
编译器会在复制(以及切割)的同时修正虚指针 —— 忘了……
要memcpy( &b, &d, sizeof(b) ); 才行……


关于【for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)】
我指的不是for each。而是遍历。
不明白为什么只需要遍历,却用了一个map。
然后…… 我再看了看这行代码之上的代码,这是一个vector……
我又看错了…… 代码看得不仔细……



关于coding-style。其实我是最不讲究这个的了……
但有些东西不属于coding-style,而是属于底线 —— 使用c/c++语言必须遵守的 —— 不能使用语言保留的标识符。
语言保留的标识符还分了几种, 统一起来就是以下划线开始的程序员全都不能使用。
这在很多书里面都有说,又在很多书(比如Gof,不知道算不算经典)中犯错。

re: GUI框架:消息检查者 OwnWaterloo 2009-11-22 05:14
貌似沙发又得被我占领……
作为一个对oo不屑一顾的c(pp)er,看到这样的代码相当不适……




比如MessageChecker和它的子类……
不适啊……

1. 切割与多态
从msvc和gcc的实现来看,虚指针是切割不掉的,它在父类中。
虚表就更不存在切割一说……
不能多态的原因不是因为切割,而是因为 —— 没有使用引用或指针类型进行调用……
所以啊,对这种情况 …… 可以恶心点……
按值保存(会被切割),同时对每个值也保存一个对应的指针,指向该值。
通过指针而非值进行调用,诱使编译器使用虚指针。

为什么感到恶心呢……
是因为这样可以多态调用,但不能保证子类的不变式……
调用的是子类的函数,使用的是父类的数据(被切割了)……


2. class vs struct
当然,对这个问题蛮适合的。
因为楼主的方案在子类中同样不能塞新的数据,否则一样会被切割。
然后,调用那个函数指针时,就会访问到无效内容。


为什么感到不适呢……
上面说了,只要是按值存储,子类就不可能添加数据成员 —— 否则就会被切割。
也就是说,无论怎样继承,数据成员就那么几个……
这里需要的其实不是class, 而是struct ……
强行使用class, 会使得需要定制的时候,就需要定义一个类 —— 其实仅仅需要定制一个函数就可以了。

struct message_checker
{

id;
wp;
lp;
int (*is_ok) (const message_checker&,const message& );

};

message_checker ButtonClickingChecker(WORD id);
message_checker xxxChecker( ... );

所以,怎么看怎么别扭……
当然,这是口味问题…… 不值得争个你死我活……


3. 其他
有些不需要id,wp,lp的checker该怎么办呢……
需要比id,wp,lp更多参数的checker又该怎么办呢……


for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)
这个效率是很低的……
_HandlerMapIter的命名也是不符合c/c++规范的…… 都被Gof带坏了……
@唐风
C++老豆的书得看~_~ 确实是对C++理解最深的人。
本地化在《TC++PL》的附录(好像特别版才有附录)中有,不过好像没想象中的那么好用……


关于log…… 特指debug log 我不是说它的效率…… 而是为什么需要这种东西……
我的想法是,能测试就不要用调试器,能用调试器就不要写debug log,只有不得已的时候,才会用那个东西。
为什么不把bug都查出来?
而是将它隐藏到软件中,等它出现后 —— 交付给用户使用后 —— 再从debug log中研究哪出的bug?

当然,我也没什么大型软件的经验,也没有用户逻辑复杂的软件的经验……
可能那时候会理解debug log的用处……

re: 函数调用栈初探 OwnWaterloo 2009-11-21 17:56
@唐风

能不能设置一个类似“勾子”的东西,在CPU进行压栈的时候记录下某些信息呢?

这个就不知道了……
查查vs的那个性能测试是怎么做到的? 也是非侵入的。
或者其他profile工具是怎么做的?

re: 函数调用栈初探 OwnWaterloo 2009-11-21 17:55
@唐风

但可以肯定的是每个函数调用都会有压栈出栈

问题就在于这个前提是没有保证的。
编译器处理尾调用时可以使用jmp而不用call。
这种情况在msvc和gcc上都存在。

编译器不一定会生成序言部分 —— push ebp mov ebp esp。
msvc肯定有这种情况,我见过。
gcc好像会严格生成这部分代码。



vs2005 team suit好像有这个功能。 在性能测试中。 但没这么灵活……
好像只能在程序跑完之后才能输出分析的结果,不能任意两个断点之间。
输出结果中记得是包含了调用树的。
可以玩玩看,也许有输出2个断点之间的调用树呢?

@唐风
这方法好,学习了~

我觉得《the c++ programming language》中21章将流的架构讲得很清楚。
具体实现么。。。
有2本书可能有这方面的内容,我没细看……
1本是《C++ Templates》
1本是《标准C++输入输出流与本地化》
http://www.china-pub.com/3274


代替io流么…… 有个iconv可以做编码转换。
代替io流的场合我没遇见过……
其实我对很多项目都要自己写一个log模块很不理解……
没往这方面想过……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-21 02:04
@Loaden
同学生日,聚会喝了点酒…… 看代码很晕…… 像不认识C++似的……

关于你在csdn上的问题。

函数模板可以通过实际参数来推导类型。
template<typename T>
T max(T v1,T v2);

max(1212 , 326); // max<int> T=int
max(1212.0 , 326.0); // max<double> T=double

max(1212, 326.0); // 歧义 T=int or double
max<double>(1212, 326.0); // 显示指定 T=double


在模板参数列表中,最后一个不能被推导的参数以及它之前的所有参数都必须显示指定:
template<typename D,typename S>
D union_cast(S src)
{

union
{
S src;
D dst;
} u = { src };
return u.dst;

}

union_cast<long long>(&C::f); // D= 一个成员指针,但S不能通过实际参数推导出来,必须显式指定。


操作符重载,其实也是一个函数,只是通常不以函数的形式调用。
所以你那个需求,可能真的只能这么写:
a.operator[]<T,U>( index );


如果真的想实现:
T* p = a[ i ];
肯定能实现,不过很恶心…… 也可能很危险……

class A;

struct proxy
{
A* a_;

template<typename D>
operator D*() const { return new D; }

};

class A
{

template<typename T>
proxy operator[](T i)
{
proxy p = { this };
return p;
}

};

A a;
proxy p = a[ i ]; // 调用a.operator[]<int>; 返回一个proxy
E<TBase>* o = p; // proxy.operator<D>

E<TBase>* o = a[ i ]; // 连起来就是这样……


template<typename T>
operator T() 是很邪恶的东西,尽量少用……



是的。因为这样才能兼容x64平台:x64前8个参数通过寄存器传递。

确实,i386是个特例,主要通过栈来传递参数。
这样,无论被partial application的函数的参数如何 —— 只要不是返回大结构体 —— 插入一个指针参数都可以使用相同的thunk结构体。

我考虑过ppc(我唯一能搞到的非i386的机器),主要通过寄存器传递。
视被partial application的函数的参数,插入一个指针需要不同的thunk结构体。
但替换某个参数,thunk结构体就和其他参数的数目无关了。

如果被partial application的函数的参数已经确定,为其设计一个thunk结构体,插入一个指针参数也应该是可行的。

boost.lambda好像只是表达式。 没仔细研究过。
要保存可以有auto。。。 或者boost.typeof。

本来我需要的仅仅是一个函数,却不得不定义一个类…… 很……
被其他语言惯坏了,就觉得c++写gui非常不方便……

我打算等c++0x流行之后再考虑是不是要用c++写gui了~_~
在哪之前,如果有得选择,c++就不是第1人选……
re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 17:19
@cexer
那篇文章我晚点再看看。

我觉得是函数指针还是函数对象关系都不大,我并不排斥函数指针~_~

关键是那个共用的模板定义不出来啊……

假设,一个xxx控件,用来调节一个整数,它提供这么一个通知:

void xxx::on_change_add( void (*listener)(void* context,int pos), void* context );

我觉得最好的语法:
void f1()
{

xxx x;
int i = 0;
x.on_change_add( void (void* c,int pos){ *(int*)c = pos; }, &i );
wait();
printf("%d\n",i);

}
应该只有等C++0x的lambda才可以这样写了。


次之:

void f2()
{

xxx x;
int i = 0;
struct local { static void set_pos(void* c,int pos) { *(int*)c = pos; } };
x.on_change_add( &local::set_pos, &i );
wait();
printf("%d\n",i);

}

这是符合C++语法的,比较丑陋了。
但如果on_change_add是函数模板,就会很麻烦。
函数模板的实际类型参数不能是这种内嵌的类型。


最难看的:
void set_pos(void* c,int pos) { *(int*)c = pos; }
void f3()
{

xxx x;
int i = 0;
x.on_change_add( &set_pos, &i );
wait();
printf("%d\n",i);

}


set_pos只是一个示例而已。
代表的是一簇简单(代码就2、3行),但逻辑多变 ——根本不可能预定义一些函数对象或者函数模板 —— 只能是随写随用、一处使用、不被复用的代码来表现需要的逻辑。

类似的例子, std::for_each如果没有lambda的支持, 就是鸡肋。


当然,这只是个审美的问题……
也有人觉得第3种(定义到外部)还不错……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 16:57
@cexer
GetClassInfo( DIALOG , &wc );
wc.lpProc = my_proc;
不修改wc.name
RegClass( &wc );

这样?

这……

如果在修改wc并注册之前,已经创建了一些窗口呢?
会怎样? 也会跟着被修改? 不会吧……?

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 16:52
@cexer
超类化?


可以创建嵌套类,嵌套类中可以有静态或非静态成员。比较间接一点。
直接写嵌套函数是不行的。

跟是否可以函数对象没什么关系。
主要需求是定义这个回调(或者函数对象)的地方最好和使用这个回调的地方比较接近。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 15:16
也不算匿名函数吧。
就是,如果一个调用需要传入一个函数作为回调,如何在调用点上创建这个回调函数,而不是在调用点所在函数外。

void handle_xxx( callback );

void f()
{


handle_xxx
( /* 1. 如何就地创建一个函数,传递给handle_xxx */
{ ... }
/* 估计C++0x才行 */
);


/* 2. 或者次一点,再handle_xxx的调用点所在函数中创建 */
my_handler () { ... }
handle_xxx( my_handler );

}


/* 3.最次的,就是在调用处所在函数外定义 */
my_handler() { ... }
void f()
{

handle_xxx( my_handler );

}

C++可以在函数内创建一个嵌套class, 并定义该class的函数 —— 可以是static的,但就是不能直接定义一个嵌套函数。
这可能是C++98、03的唯一手段了。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 15:08
@cexer
白天思维很乱…… 晚上思维清晰一些。
在想C++中怎么弄出匿名函数…… 无果……

如果没有这种东西, gui的语法是不会漂亮的……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 15:06
@cexer

GetWindowLongPtr 确实有比较大的限制。除了你所说的,对话框,按钮这类的已经注册过的窗口类,其 cbWndExtra 都已经是固定的,要想使用 GetWindowLongPtr 来存取额外数据的话,就必须要超类化,这样的就又麻烦了。所以综合考虑,SetProp 和 thunk 是最优选择。


已经注册过的窗口类,如果要塞context,都需要subclassing。
SetWindowLongPtr( hwnd, GWLP_WNDPROC, insert_context );
然后在insert_context中将context塞进去,SetProp或thunk。
只是GetWindowLongPtr塞不了。
对窗口类注册不由自己控制的情况,GetWindowLongPtr确实不适合。


对这些预定义的窗口类需要做一些什么工作,有什么需求,我没怎么细想……
用win32 api写gui的事,我没做几回……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 14:47
@cexer
关于tss+table, 我理解错了……

SendMessage( hwnd , .... );
如果调用SendMessage所属线程T1和hwnd所属线程T2不是同一个线程,则依然是由T2,而不是T1去执行hwnd的wndproc。
我想成T1去了……

哦,还有个GetWindowThreadProcessId……
如果tss真有问题,可以不用那么自动的tss,手动+GetWindowThreadProcessId也行。

那mfc又是怎么挂掉的呢……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 01:53
关于GetWindowLongPtr。


1. 系统定义index
有配套的GWLP_xxx系列…… 以前真没注意……
在32位上GWL_xxx和GWLP_xxx的值可能相同…… 但还是应该使用配套的……
自从我用Get/SetWindowLongPtr开始,就没写对一次过……
囧……

幸好cexer的某个回复中有GWLP_USERDATA……
顺手再查了一下……


2. array index
array就是一个byte的array。
这个array的地址是不会返回给用户的 —— 应该如此。

大致应该是如下伪代码:
LONG_PTR GetWindowLongPtr( hwnd , index )
{

char* array = get_extra(hwnd);
LONG_PTR r;
if ( index + sizeof(r) > get_extra_size(hwnd) )
{
SetLastError(ERROR_INVALID_INDEX); // 1413
return 0;
}

memcpy(&r , array+index , sizeof(r) );
return r;

}

越界会返回0,LastError是无效索引,1413。

有效索引应该是:
GetWindowLong, [0, cbWndExtra - sizeof(LONG) ], msdn说-4,也算对。
GetWindowLongPtr, [0, cbWndExtra - sizeof(LONG_PTR) ] , msdn说-sizeof integer, 这就是我困惑的地方……
integer是啥? short算不算…… 应该是sizeof(LONG_PTR)才对……


对齐用户(winapi的用户)也不需要特别关心。


反正array的地址是不可能暴露出来的,上面有些地方可能说错了。

SetWindowLong/Ptr也是会检查无效索引。


3. 创建窗口时收到的消息:

测试时顺便得到的:
WM_GETMINMAXINFO = 0x24;
WM_NCCREATE = 0x81;
WM_NCCALCSIZE = 0x83;
WM_CREATE = 0x1;

可能还和窗口风格什么的有关,以上不准,以msdn为准。


4. GetWindowLongPtr的其他限制
cbWndExtra在对话框中有一些规定。
MDI也有一些特殊规定 —— 针对CreateWindow是传入context。
可能还有一些特殊情况……

总之…… win32 gui是很浩瀚的事情…………
cexer…… 精神上支持你……


re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 00:44
我说一下思路吧,不算证明 tss+table不行,只是说一下这个方案可能遇到的陷阱。

假设这就是注册窗口类时使用的函数:
LRESULT CALLBACK proc(HWND hwnd, UINT msg, WPARAM, LPARAM )
{

if ( msg == WM_XXX ) // 处理某条消息时需要得到this
{
table = get_tss_table(); // 线程相关映射表
w = table.lookup( hwnd ); // 查找hwnd对应的object。
w->f() // 调用一个成员函数...
}

}

如果另一个成员函数:
C::g()
{

SendMessage( this->m_hwnd , WM_XXX , ... );
// 而且这个消息、WM_XXX、在proc处理过程中需要取得this

}

那这个成员函数g,就不能在另一个线程中使用。
因为table = get_tss_table();是线程相关的。


是否会有这种成员函数…… 就不知道了…… 没这方面的经验,也想不出例子……



mfc返回一个CWnd* ,可能也是基于这种考虑:
CWnd* w = father->getChild (...);
w->g(); // 这里可能需要查表。

mfc中也可以这样:
HWND hwnd = ::getChind( father->m_hwnd, ... );
CWnd w;
w.Attach( hwnd ); // 插入表
w.g(); // 可查询
w.Detach(); // 从表中删除

可能afx考虑到最后的w.Detach会被忘记调用。一旦w离开作用域,那个hwnd -> object的表,就指向一个无效对象。

所以就引入了一个类似gc的方式…… 返回CWnd* 同时插入表中。

纯猜测……


@codejie
因为我最近写的绝大部分是Clean C —— C和C++的子集。
所以对C和C++的区别比较注意。
还会同时拿到两种语言的编译器上去编译。


我的blog里面还有一些C/C++区别的文章。
因为学校要求写实习周记…… 我又不想随便写点应付交差……
所以就写了一些文章放到blog上, 然后再抄给学校交差……

所以那个月更新了不少…… 以后还会更新一些。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-20 00:13
@cexer
确实有点猥琐……

没这功能,WndProc的设计真的就是缺陷了……
有这功能,WndProc还是很麻烦……


我刚吃完晚饭…… 囧……
习惯没养好…… 夜猫惯了…… 慢慢改吧……
可能哪天不写代码了,生活会规律一些……


那个array的index到底是怎样,msdn我没看怎么明白……
待会验证一下……

tss + table是否可以调用成员函数, 我觉得有困难……
我再想想……
毕竟,要证明很容易 —— 想出一个实现。
要证伪 —— 证明不可能实现出来 —— 要困难不少……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 23:14
还有一句:

Remarks
Reserve extra window memory by specifying a nonzero value in the cbWndExtra member of the WNDCLASSEX structure used with the RegisterClassEx function.


原来叫cbWndExtra…… 不叫cbExtra……
很有没用…… 写错了……
也对,因为相对的还有cbClassExtra……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 23:12
@cexer
哈哈,性感诱人吧? 所以我极力推荐这妞……
也正因为误解很严重……
所以用户代码访问 [0, length ) 的几率还真不大……


抄一下msdn……

The GetWindowLongPtr function retrieves information about the specified window. 【The function also retrieves the value at a specified offset into the extra window memory.】

LONG_PTR GetWindowLongPtr
(
HWND hWnd,
int nIndex
);

nIndex
[in] Specifies the zero-based offset to the value to be retrieved. 【Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.】
To retrieve any other value, specify one of the following values.
... 后面就是其他很多系统定义index了。


GetWindowLongPtr的另一个作用 —— 0-based array —— 只有这么2句话……
还隐藏在对系统自定义index的大篇幅的介绍之中……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 23:03
@cexer
我没有细想过 HWND -> this的映射的方案……
因为它一开始就在我排除的范围内……

——【一个设计缺陷不应该由另一个设计缺陷来弥补】。

幸好,最后我发现WndProc(hwnd, ... )还不算彻底的设计缺陷。
因为hwnd可以有cbExtra。


我再想想table(hwnd,this)是否一定需要mfc那样的机制……
re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 22:57
@cexer
我一直都在强调……
GetWindowLongPtr, GWL_USERDATA是普遍被误解了的……


我从学Windows开始…… 就把这个用错了……
因为我的老师给我们的课件上用错了……
网络上【我没见过用对的文章】,也全都用错了:
p = (Window*)GetWindowLongPtr( hwnd , GWL_USERDATA );



不是这样的……
GWL_USERDATA有其本身的含义(在对话框中)。
应该怎么用还是怎么用。
库是不需要占用它的……


GetWindowLongPtr返回的是一个array中的元素。
1.
它可以是一个以0开始的,大小为cbExtra / sizeof(void*)的数组。
2.
或者是以0开始的,大小为cbExtra / sizeof(long) —— 如果以GetWindowLong取得。
3.
或者是一些系统定义的index, 比如GWL_USERDATA, GWL_WNDPROC —— 这些系统定义的index全是负数。

我上面写的代码全是:
GetWindowLongPtr( hwnd, 0 );
或者:
GetWindowLongPtr( hwnd, index );
而不是:
GetWindowLongPtr( hwnd, GWL_USERDATA );是错的 —— 对于塞this来说的话 —— 确实会占用掉用可以留给用户的机制。


库只要将注册窗口类的过程截获,并且给cbExtra增加一点点 —— padding + sizeof(this) + guard。

库的用户还是可以自由使用GetWindowLongPtr, 只要他没有越轨 —— 不超过他填入的cbExtra。
用户要越轨,那就责任自负……


内存分配:
cbExtra应该是和window一起被分配,不需要专门分配thunk结构体或者SetProp用的数据。
速度:
直接通过index寻址array,也几乎是最快的查找方式。


所以我说从整体上thunk和GetWindowLongPtr的效率可能差不多。
thunk如果one window one WNDCLASS,会注册很多WNDCLASS。
如果多个window共享一个WNDCLASS,就需要转发一次。
这些都是有效率损耗的。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 22:18
@cexer
void f( CWnd* w)
{

CWnd* d = w->GetItem( ... );
d->GetWindowTitle( ... );

}

具体是不是叫这个名字不记得了。
由一个窗口得到上面的控件的窗口,然后取一下标题。
如果w来自另一个线程 …… 等着程序挂吧……


d是一个指针,有主动释放吗?
mfc的消息循环还会在odle时清除这种"临时对象"的指针。
就为了满足它的table机制,需要在动态内存中创建对象,并使用类似gc的机制去清理……
mfc的大、慢就是这么一点一点的积累出来的。
table这种方式绝不可取……


这个例子只是举例,可能并不正确,因为我很久都不用mfc了……
太out……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 22:07
@cexer
如果不考虑效率,最好的办法就是不用C++做gui了。
开发又慢又容易出错,何必呢? 图什么?


GetWindowLongPtr的问题真的很容易解决。
只要程序员用这个函数之前,看过GetWindowLongPtr的文档。
如果一个库的用户绕过库并且不看文档而直接使用win32api,库是怎么都做不到,也花太多心思防止他用错的。

他甚至可以不用看这个gui库的文档 —— 因为你可以把this放在array的最后,而不是最前。
甚至还可以在this前后设置一些guard来检测使用库的人员是否出轨。
并且,如果将this放在最后,正好也可能需要一些padding来对齐。



SetProp和thunk的效率就不用比了……
同样要分配内存。
thunk是直接调用,SetProp调用之后还有活要干……
当然,效率不是最主要的…… 也不是可以完全不顾的……


我觉得将this放在GetWindowLongPtr所在array最后,既安全又快速又简便……


当然,最终权衡在你。
而且…… 只要这个方式不对用户公开……
随时可以改……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 18:07
@cexer

thunk 完成的功能只是消息从系统到窗口类的很小一步,也是最需要完成的第一步。在这一步上我尝试过很多方法,包括这种 thunk 技术,甚至还用过TLS做映射。我目前用的方法很简单就是 SetProp,GetProp 两个函数,有点重剑换木剑的感觉。效率上肯定没有 thunk 的直接调用高,但是心里更踏实一点,在内存当中写二进制代码总有在犯罪的感觉。

“过早的优化是一切罪恶的根源”。基于这一步只是整个GUI框架当中是很微小的一步,几乎不影响框架设计,可以先把框架搭好,再来从安全,效率,可移值性各个方面进行考虑。反正不要选择 GetWindowLong ,GWLP_USERDATA 那一套,如果发布出去后客户也使用这个东西就一切全完了,“过时的悲观毫无益处”。


我就知道……
大多数人对GetWindowLongPtr/GWL_USERDATA的理解是有误的。
你先仔细看看msdn是怎么介绍cbExtra、GetWindowLongPtr,还有GWL_USERDATA再考虑要不要否定这套方案。


线程局部存储映射表?
先不说效率,mfc不能在线程之间传递CWnd*,就是一个使用上的劣势。


mfc需要在文档中记录: 不能将CWnd*传递到另一个线程。
使用SetProp需要在文档中记录:不要SetProp("cexer" , ...)
使用GetWindowLongPtr需要在文档中记录:不要SetWindowLongPtr( hwnd, 0, ... );


3种方案有区别吗?文档中都必须有记录 —— 什么是库使用的,用户不能随意使用 —— 没一个逃得掉。
可能只有thunk,不需要在文档中特别说明什么,比如ATL/WTL。

区别还是有的,效率。
tss-table肯定是最低,编程也最复杂的,完全不值得使用。
SetProp需要查找,效率肯定没有另外两种高,而且,它很可能也需要分配内存。

thunk和GetWindowLongPtr旗鼓相当。很难说谁高谁低。
但后者要简单很多,而且是在所有Windows上都可以使用,不用考虑汇编的问题。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-19 17:56
@Loaden

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

你用的是和atl一样的方式?
将hwnd"替换"为this? 而非"插入"一个this?
是吗?

1~3cpu指令只是"调整栈"而已,调整完了,肯定会继续执行thunk中的call,来到你的函数。
这个call或者jmp是免不到的。
同时,编译器得不到thunk的目的地的信息,所以不可能进行任何优化。
本来也没什么优化的余地了,都机器码了……


x64的我也有点想做……
但对x64上的汇编和调用约定不熟悉,而且也没有x64的测试平台……
@codejie
我也是看那篇文章才懂的……
值得看看,推荐~_~

typedef struct DefStruct DefStruct;

typedef BOOL (*func)(const DefStruct* s);

struct DefStruct
{

int i;
func f;

};

这是tag在C和C++中的区别造成的问题。
更详细的,见这里:
http://www.embedded.com/9900748

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 16:08
@陈梓瀚(vczh)
我就一闲人……

我和罗登在gtalk上聊得更欢……
要不要来插一脚?

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 16:00
@Loaden

纠正一下,是随手 三 测 ^)^
第三次改输出代码时崩溃的。


随手1测就错了,非常不靠谱……
随手3测就错了,稍微靠谱一点点……
反正代码很不靠谱的,免责声明我也写了的……
出了任何问题我不负责哦~_~

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 15:59
@Loaden

#define EVTCREATE(wnd, wpa, lpa) \
case WM_CREATE: \
{ \
evt::Create evt = {wnd, this, reinterpret_cast<LPCREATESTRUCT>(lpa), false}; \
...

this是怎么得到的?


LRESULT CALLBACK Frame::WndProc(UINT msg, WPARAM wpa, LPARAM lpa)
{
...

这是静态还是非静态的? 函数定义看不出来,只有声明才行。
估计也是非静态的吧? 因为有m_wnd这些东西。
那么,this是怎么得到的??



追根究底,要从【注册窗口类】时给它的【WndProc】查起。
如果你传递给它的不是一个真正的函数,而是一个thunk,这个thunk就会被调用。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 15:45
果然没有错误处理是邪恶的……

经过Loaden的强力测试(其实也就随手一测),崩了……

先上代码:
time_t t1 = time(0);
tm t2 = *localtime(&t1);
t2.tm_hour = 348294832;
t1 = mktime(&t2);
printf("%d\n",(int)t1);
tm* pt3 = localtime(&t1);
printf("%p\n",(void*)pt3);

t2.tm_hour = 348294832; // 这里对于32位的int来说,没有溢出。
但hour的"权值"比较重,最后转化为time_t的表达可能会溢出。


t1 = mktime(&t2);
printf("%d\n",(int)t1);
C89要求:mktime如果不能将tm转换到正确的time_t格式,就返回(time_t)-1。

而vc8的运行库不是这么处理的,它会直接崩掉,而不是返回-1。
触发的断言是:
loctime64.c (78)
(*ptime <= _MAX_TIME64_T)

所以,在vc8下,mktime那行代码就会崩。
在vc9、mingw3.4.x下,返回-1。


tm* pt3 = localtime(&t1);
printf("%p\n",(void*)pt3);
如果time_t不是正确格式,C89好像没说应该怎么做……
vc8、vc9、mingw3.4.x一致返回空指针。


再看date的代码:

int get_(int tm::*field) const
{

return localtime(&impl_.time_)->*field;
//当time_t不正确时,返回空指针,然后被解引用,崩。

}

date& set_(int tm::*field,int v)
{

tm t = *localtime(&impl_.time_);
t.*field = v;
impl_.time_ = mktime(&t);
// 当t格式不正确时,vc8不会返回-1,而是直接崩。
return *this;

}


错误处理还是需要的……
不过这仅仅是示例代码,还是不加了吧……
以免影响清晰…… 突出重点……

感谢Loaden的测试~_~

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 15:05
@Loaden

thunk不用调用,直接处理消息后,调用默认窗口过程。

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

只是我没看出你的代码中哪句是调用……


我喜欢将什么东西都放在类里。

最大的毛病:这会将优秀的算法埋葬到类中。


强调一下,是"零"代价,不是低代价~_~
union base, union member是低代价的。
re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 14:31
@Loaden
忘写了…… 联系方式,同名gmail邮箱……


CWindow* w = (CWindow*)GetWindowLongPtr( ... );
这个调用不仅仅是返回一个值而已。
而且不同的窗口类,要返回不同的值。

你的意思应该是,不同窗口“实例”,返回不同的值,是这样吗?
那就对了,GetWindowLongPtr( hwnd, ... );本来就是不同窗口实例返回不同的值嘛……



所以,这里编译器如何优化呢?

我说的优化是它下面的一个调用。
w->handle( ... ); // 可能会被inline,避免参数传递。

因为编译器知道这里肯定调用的是handle,不会是其他函数。

但thunk,调用的地址是运行时的,所以编译器无法优化。



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

先把GUI框架稳定下来再说。
谢谢你的指点!如果有好的解决thunk的内存池(ATL就是这么干的,只是不开源),请介绍一个给我。

其实 …… atl的源代码是能看到的…… 至少thunk部分是这样。
我上面有一个回帖, 估计你看掉了。
如果只想支持win32, 可以用HeapAlloc那套。
一个版本中的atl就是这么干的,还有个版本是自己实现的池 —— 具体记不太清楚了。


如果想在posix平台下,得到分配可执行内存的高效的池 ……
等过年吧,哈哈哈



而thunk,从其汇编代码可以看到,代价只是多花1~3条CPU指令而已。
显然要高效。

你看到thunk的分配了吗? 考虑到thunk的复杂性了吗?
要全局的看~_~
cbExtra可以被实现得很高效。当然,我只是猜……



另外,static函数、全局函数也不利于C++封装。

free-function才是范化形式……
member-function只是free-function的一个特例。
是否封装,是看你是否仅通过被数据认可的操作去使用数据,而不是自己去拨弄数据。
跟语法无关。 语法只是个便利。

这个话题…… 估计要扯很远了……
你不接受很正常,我可以理解……
我只是提一下……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 13:54
@Loaden

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


static 函数怎么影响效率了? 多传递了一次参数?
有缺陷的回调肯定是C的,参数中肯定都是bitwise-copy。
所以,还好吧……

其实你需要的就是一个context而已。
thunk, Set/Get WindowLongPtr/Prop 都是设置/得到context的手段。

thunk的效率不见得比Set/Get WindowLongPtr高,而且用着很麻烦。

WndProc(HWND hwnd,UINT msg,WPARAM w,LPARAM l)
{

CWindow* w = (CWindow*)GetWindowLongPtr( ... );
// 编译器知道这个调用处的上下文,可以进行优化
return w->handle(hwnd, msg, w, l );

}


thunk的话,要么每个窗口对应一个窗口类,该窗口类对应一个thunk。
要么若干窗口对应同一个窗口类,每个窗口再
SetWindowLongPtr(hwnd, GWL_PROC , ) 去改窗口处理过程。

调用一个chunk是绝对得不到优化的,因为编译器根本就不可能知道thunk的目的地在哪。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 13:44
@Loaden
兄弟,你知道这会分配多少吗?

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


这里用new placement也不太妥当。

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 13:42
补充一下: 关于DEP ,做thunk可能会用到。

win32可以用win32 堆(HeapAlloc那套),创建时可以指定内存权限。
这几乎是最省心的做法了。

不过我想做posix移植(论文啊…… thunk已经烂大街,不拿点新东西出来怎么过得去……)
所以就要自己实现 ……
做着做着嘛…… 发现内存池可以再分出几部分……

比如其中的一部分是intrusive data structure。
小对象内存池其实只是intrusive date structure的使用方式之一。

然后打算重构,然后…… 就毕业了…… 然后就无限延期了……

过年之后再做吧……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 12:38
@Loaden
对框架设计,我不熟…… 因为一般来说,我讨厌那个……

为什么?
框架和类库有什么区别?
类库是帮助你的,框架是限制你的……

作为一个框架的设计者,他必须对这个领域十分了解:哪些过程是常用的,哪些是有害的。
才能准确的做出限制。
我对gui编程经验不多,所以就不做这方面的活了……



说说win32下gui库/框架必须解决的一个问题吧: user_data,或者叫context。
因为那个万恶的WndProc没有传给你……

thunk是一种办法,一种i386(其他平台我不知道怎么实现)上通用的办法 —— 只要是因为回调设计不正确,没有带context参数,就可以用thunk解决。
实际上…… 我毕业设计就是做的这个……
__stdcall也不是必须的…… __cdecl也可以……
比如qsort, 它就是需要一个 __cdecl的。

这里有一部分代码:
http://code.google.com/p/callback-currying/

主要是做自由函数的thunk,而不是成员函数。
一方面是可以给C程序员用。
第2个原因是因为this和成员函数指针的可移植性很糟糕。
第3个原因是因为可以通过自由函数,转发到成员函数中,对成员函数的支持不是绝对必要的。

直接thunk成员函数有一个好处。我论文里面有提到。
返回一个大结构体的函数,直接thunk成员函数可以,thunk自由函数不行……

stdcall2stdcall 需要12字节
cdecl2cdecl 需要23字节
thiscall2stdcal(msvc) 需要10字节
其他记不清楚了


设计好thunk结构体,只是关键技术之一。
之后还有很多很多的麻烦…… 主要是DEP(数据执行保护)

在win32下可以用VirtualAlloc来分配可执行内存,posix下使用mmap。
但是…… 不可能为了这么小一块内存,就去分配1整页吧? win32下还要保留64k……
内存池是必须做的…… 而且现有内存池都无法被复用

所以,那个gcode后来就没有维护了。
svn管理实验代码很麻烦…… 转git了……

最后的版本还没有敲定,等过年后我会发一个版本出来……

named template parameter就是为了解决callback currying中模板参数混乱(不仅是多,真的是混乱,论文里面好像有详细解释)而设计的。



跑题了…… 上面的中心思想是: thunk是一种通用技术,但麻烦多多。
如果仅仅是为了解决WndProc, 有专有技术。
Set/GetWindowLongPtr
还有前几天学到的…… Set/GetProp
见这里:http://www.cppblog.com/kyelin/archive/2009/11/13/100880.aspx

需要注意的是,Set/GetWindowLongPtr网上很多文章都说错了。
具体的使用方式去查msdn。
cbExtra是一个0 based array —— 我记得msdn是有这么一句话的。

GWL_USERDATA是另外一回事。


用这2种方案之一就可以了(或者win32还提供了其他附加数据的方案?)。
mfc的线程局部映射表…… 哎……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 12:19
@矩阵操作
union里面好像不能出现private的东西。具体得查标准……

我那个,也是权宜之计……
客户代码访问不了这个类型:date::impl
但可以被推导……
template<typename T>
void set_default(T& v) { v = T(); }

date d;
set_default(d.impl_); // 挂了


不过,private也不是万能的……
指针算术,宏,都有可能让它失效。
取名为impl_, 应该能起到警示作用 —— 不要使用这个名字 —— 吧……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 04:43
为了避免vczh同学误会……
我再申明一下……

1. 这是彻底的玩具代码
所以对日期的合法性不作检查……

2. 代码还需在顶部加入两行:
#include <time.h>
#include "property.hpp"
才算完整

3. "property.hpp"
的搜索路径需要自己配置,或者就和date.cpp放一起。

4. 需要在<time.h>前加入:
#define _CRT_SECURE_NO_DEPRECATE
才能避免msvc上常见的4996警告。

5. 或者另一种方式,代码行数较多:
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif

6. main的return 0可以省略
因为一定是C++代码,也不用兼容vc6



能想到的我都说了……
你就绕了我吧……

re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-18 04:18
差不多敲定方案了,贴下代码吧:
 
class date
{
        
class impl
        {
                time_t time_;
                friend 
class date;
        };

        
int get_(int tm::*field) const { return localtime(&impl_.time_)->*field; }
        date
& set_(int tm::*field,int v)
        {
                tm t 
= *localtime(&impl_.time_);
                t.
*field = v;
                impl_.time_ 
= mktime(&t);
                
return *this;
        }

public:
        date() { time(
&impl_.time_); }

        
int get_hour() const { return get_(&tm::tm_hour); }
        date
& set_hour(int h) { return set_(&tm::tm_hour,h); }

        
int get_minute() const { return get_(&tm::tm_min); }
        date
& set_minute(int m) { return set_(&tm::tm_min,m); }

        
int get_second() const { return get_(&tm::tm_sec); }
        date
& set_second(int s) { return set_(&tm::tm_sec,s); }

        union
        {
                impl impl_;

                property::proxy
                        
<property::get<date,int,&date::get_hour>
                        
>               hour_read;

                property::proxy
                        
<property::set<date,date&,int,&date::set_hour>
                        
>               hour_write;

                property::proxy
                        
<property::get<date,int,&date::get_minute>
                        ,property::
set<date,date&,int,&date::set_minute>
                        
>               minute; // read write

                property::proxy
                        
<property::set<date,date&,int,&date::set_second>
                        ,property::
get<date,int,&date::get_second>
                        
>               second; // read write
        };
};

int main()
{
        typedef 
int ASSERT_EQUAL_SIZE[sizeof(date)==sizeof(time_t)?1:-1];

        date d;
        
volatile int v = 1212;
        
        v 
= d.hour_read;
        d.hour_write 
= v;
        
// v = d.hour_write;
        
// d.hour_read = v;

        v 
= d.minute;
        d.minute 
= v;

        v 
= d.second;
        d.second 
= v;

        
return 0;
}

 
这次首先重点show出来:
1. 0代价实现proxy
typedef int ASSERT_EQUAL_SIZE[sizeof(date)==sizeof(time_t)?1:-1];
 
2. 定义property的语法也还不算太坏吧?
使用一种"named template parameter"技巧,将read、writer、read-write合体。
hour_read, hour_write不用解释了。
second,minute需要解释一下:set和get出现在proxy的参数中出现的次序是随意的。
所谓named parameter是也。
 
不要拿hour_read用于写或者拿hour_write用于读,报出的错误可能很难读。
有空闲再处理报错好看问题吧……
 
 
 
proxy的其他技巧有请vczh同学介绍。
 
 
 
 
关于union中不能放非pod的问题……
1. 基于C-API来构造自己的类
那自然没什么问题。
如果还想进行访问控制,就像date::impl一样,包到一个private nested class中,并设置外部class为friend。
 
依然0代价。
 
 
2. 基于现有的非pod来构造自己的类
楼上已经提到了union base和union member两种方案。
不过property::proxy不支持这种用法,得手工实现……
遇到的问题也是楼上提到的"先鸡还是先蛋"……
为了解决鸡蛋问题,想了很久都没想出可以很清晰定义property的方案……
 
换种思路:使用boost.aligned_storage + placement new + 直接调用析构函数吧……
也算一种解决办法了…… 而且也是0代价的。
 
 
 
玩具代码,仅为展示上面的2个重点技术而已。实际应用还需自行编写。
property我以前也没用过…… 没什么经验,不知道通常会怎么使用。
 
测试编译器:msvc8、9、10,gcc3.4.x。
写命名模板参数就没打算支持vc6……  不测了……
gcc4.x懒得切换系统了…… 应该没什么问题……
 
re: GUI框架:谈谈框架,写写代码 OwnWaterloo 2009-11-17 21:28
@visualfc
谢谢分享~_~

英文…… sigh……

共10页: 1 2 3 4 5 6 7 8 9 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(8)

随笔档案(16)

链接

搜索

  •  

积分与排名

  • 积分 - 196688
  • 排名 - 132

最新随笔

最新评论

阅读排行榜

评论排行榜