- 论坛徽章:
- 2
|
原帖由 windyrobin 于 2009-3-28 16:19 发表 ![]()
无论sgi 还是vc 的stl map/set ,都是提供了最少的参数列表。
(stl 的 sort 应该也是基于这种考虑的吧)
我觉得不是这样。
支持回调模式的技术有很多, pointer to function, functor, interface, delegate, signal/slot 等。
pointer to function和其他的技术有非常重要的不同 —— 被指向的函数, 不含有状态(或者叫信息)
而其他技术, 加入信息是很容易的。
比如我在上一楼又复述了一次的例子, 如果使用std::sort 而非qsort, 就可以很容易加入额外信息, 比如:
class sort_by_course : public std::binary_function< student, student, bool > {
course_category c_; // 包含额外信息
public:
explict sort_by_course(course_category c) c_(c) {}
bool operaotr() ( const student& s1, const student& s2) const {
return s1.marks[ c_ ] < s2.marks[ c_ ]; // 使用额外信息
}
}
void sort_by_course( student students[], size_t count, course_category category ) {
std:: sort( students, students + count, sort_by_course( category /*插入额外信息*/ ) );
}
如果利用boost:: bind 或者lambda表达式, 可以做得更漂亮一点。
但是, pointer to function不行, 它不含有状态。
而且, 无论是stl的bind机制, functor机制, 还是boost的bind机制, lambda表达式(或者是c++0x直接支持的),都不能提供帮助。
因为, 它们产生的闭包,只能被函数模板所接受, 不能当作一个函数指针传递给qsort。
所以, 我觉得, 一个设计良好的回调函数, 其签名, 应该带有维持状态的参数。
这样, 按照该签名实现的回调函数,其功能才是完备的。
原帖由 windyrobin 于 2009-3-28 16:19 发表 ![]()
我觉得,在设计中:
遇到非正常情况,应保证用户自己可以通过非正常方式解决,
我们来看看能够怎样解决那个问题。 我还是用 sort_by_course的例子可以吗?
在 http://code.google.com/p/callback-currying/ 中有一个sort_by_course的sample。
1. 很容易想到的就是, 为每一个特定的比较, 实现一个函数 :
cmp_chinese, cmp_math , cmp_english ....
然后 sort_by_course 中用一个表, 或者switch。
( 见sample中的 similar_comparison )
这样的问题是, 重复。 每一个函数除了缺失的category, 其他部分都是相同的。
重复会导致的问题就是, 一处修改, 处处修改。
当然, 对这个简单的例子, 它应该是不用修改的。 实际情况它也许比较复杂。
对于这种比较准则的回调函数, 也不应该有太复杂的逻辑。
如果我们不仅仅讨论比较准则, 而是讨论回调函数应该如何设计,适用性才更广泛, 就假设回调逻辑会变得很复杂吧 ……
2. 如何避免重复 ??
在sample中, 有另外2个 similar_comparison_by_macro similar_comparison_by_template
分别使用了宏和模板来消除重复。
3. 这样还有问题 : 源代码没有重复了, 函数体仍然有重复。
最终包含的机器码的大小和b]similar_comparison依然是没有区别的。
如果我们仅仅使用一个
int cmp_generic( void* user_data, const void* s1, const void* s2 ) 来实现回调逻辑。
那么, 比较逻辑就只有一份机器码了。 (再次说明, 假设回调逻辑会变得很复杂)
然后为cmp_chinese, cmp_math , cmp_english .... 分别实现成短小的转发函数。
qsort 最终接受的是上面那几个转发函数。
这样就将重复减少到最小。
具体见sample : forwarding_function, forwarding_function_by_template
3. 当然, 也可以使用全局静态变量。
这可能是最快速的解决方法了。 这个就没有sample了。
但是, 全局静态变量也有一些问题 :
多线程? 要使用线程局部存储。windows我知道是支持的, linux我不了解了……
而且,有个最严重的问题!
在qsort中不能体现出来(所以没有为全局静态变量这个方案做sample)。(但是如果要写能体现出这个问题的sample, 又肯能会复杂得不适合做sample)
——如果你不知道回调函数的被调用时机, 全局静态变量的方法就不能使用。
比如 : WndProc (真是一个糟糕的设计 ………………)
那么, 对于一个比较通用的回调函数设计(不仅限于比较准则), 是否应该带有状态维持参数, 才是良好的设计??
想想 WndProc , HookProc为我们带来了多大的麻烦 …………
原帖由 windyrobin 于 2009-3-28 16:19 发表 ![]()
但不应单单为了照顾非正常情况,引入非正常的接口。
恰恰相反。 我觉得加入维持状态的参数, 才是正常情况。
不使用这个参数, 才是非正常情况。
不使用这个参数, 一般只能完成一些很简单的工作。
比如还是那个项目中, 有一个自己捏造的 retrieve的sample。
设计的回调函数签名如下:
int ( stdcall * retrieve_fn)( node* );
这即使一个不带状态维持参数的回调函数签名。
在这种设计下, 仅能实现一些很简单的工作, 见 simple_task 。
全局静态变量是一种解决办法,( 见 simple_variable), 同时,也不会出现上面说的严重问题——不知道被调用的时机。
全局变量么 …… 总是能避免就尽量避免吧 ……
这样的接口, 对于hash map的是否合适, 决定权还是在你手上, 我只是提个建议。
不, 建议都算不上, 我只是有点疑问 …… (因为我确实不懂, 没有大量使用hash map的经验), 还请指教。
这样的接口, 真能满足所有需要吗? 或者, 大部分的需要吗?
如果你所说的非常规情况会出现得很多吗?
出现了, 能很方便的处理吗?
[ 本帖最后由 OwnWaterloo 于 2009-3-29 00:51 编辑 ] |
|