首页
社区
课程
招聘
[原创]用FOR_EACH学习空空运算
发表于: 2011-1-26 13:02 8303

[原创]用FOR_EACH学习空空运算

2011-1-26 13:02
8303

用FOR_EACH学习空空运算

上一次我出了一道题:
        http://bbs.pediy.com/showthread.php?t=127740
        一道给C++高级程序员的题————写FOREACH

居然没有一个人肯认真做。

有一个人回答,用
#define FOREACH BOOST_FOREACH
没错,我出这一道题就是因为看了BOOST,发现它居然实现了一个这么奇妙的功能,我根本想不到用C++居然能这么做!
所以才希望大家能研究一下,究竟怎么样用C++实现这么一个貌似不可能实现的功能。

可惜居然没有一个人肯认真研究这个问题。

很多回贴都是说,意义不大。

什么叫意义大?什么叫意义不大?

这是一道题,你不会做,这就是意义。

这道题,会用到大量的模板运算,你做了它,就会学到这些东西,这就是意义。

如果你是高级程序员,你应该知道如何下手,怎么做,给你时间你总能做出来,而不是一筹莫展。这就是意义。

我希望大家踏实一点,多学一点C++基础,有好处的。

还有人说,这有什么用?单是这个FOR_EACH,已经很方便了。它比std:for_each方便多了,即使加了lambda支持,你试试
就知道了。

也许会有人说,我把BOOST代码抄下来不就行了吗?BOOST代码太复杂了,include关系复杂,如果真把实现FOR_EACH用到的
东西都抄下来,会有几十KB甚至更多。抄下来了你还是没有搞明白。

接下来,就让我们一点点研究它的实现。我是花了多天的时间,把BOOST代码一点点简化才得到的。不断感叹世间竟有如此
聪明之人,想出这么绝妙的主意。

因为VC6的模板功能问题太多,请使用VS2005以上编译器测试代码。

第一章 如何区分数组类型?

为了研究这个复杂的问题,我们把问题先简单化。第一个小问题,如何知道一个变量的数据类型是不是数组?

是不是一筹莫展,毫无思路?

原来,解决方法,是用模板的特例化功能。

模板的编译期运算,我给它起个名字叫“空空运算”。我们先引入空空运算的基础,两个常量 true_ 和 false_ :
template< bool C_ > struct bool_
{
        enum { value = C_ };
};

typedef bool_<true> true_;
typedef bool_<false> false_;
怎么理解这几行代码?定义了一个空的结构,空的模板,实在看不出它有什么用。但这,就是整个空空运算的基石。

附件中的1.cpp,演示了用模板的特例化功能,区分一个数据类型是不是数组。

请仔细体会以上代码。

苏州 WinMount 继续招C++程序员,请把简历发到 support@winmount.com


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (15)
雪    币: 468
活跃值: (340)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
谢谢 NeverGone ,已下载《Modern C++ design》和loki,正在学习
这便是讨论的收获。

第二章 尝试支持数组

废话不多说,请看附件 2.cpp ,这里用define 实现了基本的 FOR_EACH,

#define FOR_EACH2(VAR, COL)                      \
        auto_any_t _cur = BAIL::begin11( COL );      \
        auto_any_t _end = BAIL::end11( COL );        \
        for (bool _conti = true;                     \
        _conti && !BAIL::done1( _cur, _end, COL);    \
        _conti ? BAIL::next1( _cur, COL) : (void)0)  \
    { _conti = false;                            \
        for (VAR = BAIL::deref1(_cur, COL); !_conti; _conti = true)

void test()
{
        int tbl[] = {1,2,3,4,5,6};
        FOR_EACH2( int i, tbl )
        {
                std::cout << i << ' ';
        }}
}

说明几点:
        * 因为FOR_EACH后面是一个{},显示不可能定义为一个template或函数,只能是define
        * FOR_EACH的第一个参数是 int i,如果写成通常的for,那么怎么写 i++ 呢?显然
        做不到。不得已只好用双for,每次都给int i 赋值
        * 加了个 _conti有两个用途,一是 inti 所在的for只能允许它运行一次。二是当它
        break时上一个for也能知道并退出。就这么复杂了
        * 我们可以写一个begin11的模板,返回一个 iterator1<T>::type 的数据类型。但一
        旦从模板出来,用什么样的数据类型去保存它呢?boost的做法,是转为 auto_any<T>,
        然后存到一个 auto_any_t 中,使用的时候用 auto_any_cast 可以转化回来。这个过程
        我至今还没有理解。
       
最后,用这个方法写出来的 FOR_EACH 有两个缺点,一是前面不能加if,它虽然看起来是一行,
实际却是多行。二是后面必须多写一个 } 号。因为用了双层的 for

你知道怎么解决这两个问题吗?请看下一章
上传的附件:
2011-1-26 13:02
0
雪    币: 468
活跃值: (340)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
第三章 尝试支持数组之二

为了解决上一章提到的两个问题,增加一个set_false函数
inline bool set_false(bool &b)
{
    b = false;
    return false;
}
使用了一个技巧:
        if (int i = 0) {} else

见附件 3.cpp 至此已经完全支持数组了:

void test()
{
        int tbl[] = {1,2,3,4,5,6,7};
        FOR_EACH( int i, tbl )
        {
                std::cout << i << ' ';
        }
}

且支持 break, continue, 前if

接下来,尝试增加对 std:list, "hello"的支持
上传的附件:
2011-1-26 13:03
0
雪    币: 468
活跃值: (340)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
4
第四章 支持std:list

稍作修改,支持 std:list, std:vector, std:deque
void test()
{
        std::vector<int> m;
        m.push_back(10);
        m.push_back(20);
        m.push_back(30);
        FOR_EACH( int i, m )
        {
                std::cout << i << ' ';
        }
}

接下来,尝试增加对 "hello"的支持
上传的附件:
2011-1-26 13:05
0
雪    币: 468
活跃值: (340)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
5
第五章 支持C++字串

继续修改,支持 null-terminated C-style strings,最后,我们的宏支持所有以下情况:
void test()
{
        int tbl[] = {1,2,3,4,5,6,7};
        if (1)
        FOR_EACH( int i, tbl )
        {
                std::cout << i << ' ';
        }
        std::cout << std::endl;

        std::vector<int> m; // also support std::list, std::deque       
        m.push_back(10);
        m.push_back(20);
        m.push_back(30);

    FOR_EACH( int i, m )
    {
        std::cout << i << ' ';       
    }
        std::cout << std::endl;

        const char * hello = "hello, world\n";       

    FOR_EACH( char ch, hello )
    {
        std::cout << ch;
    }
}

代码见附件,不知道还能不能更简单地实现。

关注的人太少了,不再更新....
上传的附件:
2011-1-26 13:06
0
雪    币: 63
活跃值: (17)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
Template Metaprogramming, 更多像智力玩具

template< bool C_ > struct bool_
{
  enum { value = C_ };
};

typedef bool_<true> true_;
typedef bool_<false> false_;

类似这种代码,利用模版特化萃取类型信息,SGI STL源码里到处都是。
LiuTaoTao这么有兴趣,可以看看《Modern C++ design》
2011-1-26 14:15
0
雪    币: 140
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
字符和容器可以实现,但是数组自动计算大小搞不定
2011-1-26 15:28
0
雪    币: 357
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
呵呵,每个人知识面不同,原来LZ精力和研究方向主要是汇编,现在突然接触比较有技巧的C++模板有感慨是很正常的。
要鼓励分享这种渐进的学习日记。
2011-1-26 18:02
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
9
刚看了那道题,被虐了。表示一直用的C,对模板神马的一窍不通……
2011-1-26 18:12
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
10
跟着涛哥走.
#include <iostream>

// 定义一个名为 bool_ 的模板类
// 只有一个枚举类型的成员 value
template< bool C_ > struct bool_
{
	enum { value = C_ };
};
// 定义两个类型, 当使用时编译器会将其特例化为具体的类
typedef bool_<true> true_;
typedef bool_<false> false_;

// 定义另一个模板类 is_array ,派生于 false_ , 这是 is_array 的普通版本
// 只有在特例化请求不符合以下的所有情况时,编译器才将其与普通版本相匹配
template< typename T > struct is_array : false_ {  }; /* 版本A */
// is_array 的局部特例化版本,派生于 true_ , 对应的特例化请求是 char[1], int[2], double[3]... 
template< typename T, int N > struct is_array< T[N] > /* 版本B */
: true_
{     
}; 
// is_array 的局部特例化版本,派生于 true_ , 对应的特例化请求是 char const [1], int const [2], double const [3]... 
template< typename T, int N > struct is_array< T const[N] > /* 版本C */
: true_
{     
}; 
// is_array 的局部特例化版本,派生于 true_ , 对应的特例化请求是 char[], int[], double[]...
template< typename T > struct is_array< T[] > /* 版本D */
: true_
{ 
}; 
// is_array 的局部特例化版本,派生于 true_ , 对应的特例化请求是 char const [], int const [], double const []...
template< typename T > struct is_array< T const[] > /* 版本E */
: true_
{ 
}; 
// vc6 不支持模板偏特化(也叫局部特例化或部份特例化)
// 需要使用 vc7.1 或以上的编译器
int _tmain(int argc, _TCHAR* argv[])
{
	if(is_array<int>::value == false) /* 使用版本A */
		std::cerr << "int test ok" << std::endl;

	if(is_array<int*>::value == false) /* 使用版本A */
		std::cerr << "int* test ok" << std::endl;

	if(is_array<int[]>::value == true) /* 使用版本D */
		std::cerr << "int[] test ok" << std::endl;

	if(is_array<int[2]>::value == true)/* 使用版本B */
		std::cerr << "int[2] test ok" << std::endl;

	if(is_array<const int[2]>::value == true) /* 使用版本B */
		std::cerr << "const int[2] test ok" << std::endl;

	if(is_array<int const [2]>::value == true) /* 使用版本E */
		std::cerr << "int const [2] test ok" << std::endl;

	return 0;
}


2011-1-26 18:26
0
雪    币: 38
活跃值: (67)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
看着这些代码,我好纠结
2011-1-30 00:36
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
12
俗话说:女为悦已者容。
2011-1-31 21:48
0
雪    币: 451
活跃值: (78)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
13
虽然看不懂,进来支持1下涛哥
2011-1-31 22:57
0
雪    币: 196
活跃值: (135)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
14
每看一次涛哥的贴子都是一次进步,支持一下~
2011-2-1 02:07
0
雪    币: 202
活跃值: (543)
能力值: ( LV11,RANK:188 )
在线值:
发帖
回帖
粉丝
15
学习cntrump,支持涛哥.
刚看完第一楼,看到[n]来区分是否为数组就高兴的在注释的,才发现cntrump大侠先下手为强了。
回帖支持!
2011-2-4 21:38
0
雪    币: 1140
活跃值: (3456)
能力值: ( LV12,RANK:385 )
在线值:
发帖
回帖
粉丝
16
其实我也挺喜欢C++的模板的。
标记,供以后学习,现在不知怎吗的还没有心情去学习这个。
2011-2-17 18:07
0
游客
登录 | 注册 方可回帖
返回