A macro for looping map

References:

  1.  http://herbert.the-little-red-haired-girl.org/html/gcc/cpp_1.html
  2. http://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-with-positioning-macr
  3. https://github.com/airekans/Snippet/tree/master/cpp/foreach 代码

Definition

#define CONCAT_INDIRECT(a, b) a ## b
#define CONCAT(a, b) CONCAT_INDIRECT(a, b)
#define CONCAT_LINE(x) CONCAT(x, __LINE__)

#define map_foreach(key, value, m) \
for (bool __loopFlag = true; __loopFlag; __loopFlag = false) \
    for (BOOST_AUTO(__i, (m).begin()); __i != (m).end(); ++__i, __loopFlag = true) \
	for (BOOST_AUTO(key, __i->first); __loopFlag; __loopFlag = false) \
	    for (BOOST_AUTO(value, __i->second); __loopFlag; __loopFlag = false)

上面的这个macro是不能处理break的,continue可以处理。


下面这个是可以处理break的版本。

#define MAP_FOREACH(key, value, m) \
for (bool CONCAT_LINE(__f) = true, CONCAT_LINE(__breakFlag) = true; CONCAT_LINE(__f); CONCAT_LINE(__f) = false) \
    for (BOOST_AUTO(CONCAT_LINE(__i), (m).begin()); \
		CONCAT_LINE(__breakFlag) && CONCAT_LINE(__i) != (m).end(); \
		++CONCAT_LINE(__i), CONCAT_LINE(__f) = true) \
		for (BOOST_AUTO(key, CONCAT_LINE(__i)->first); CONCAT_LINE(__f); CONCAT_LINE(__f) = false) \
		    for (BOOST_AUTO(value, CONCAT_LINE(__i)->second); \
				CONCAT_LINE(__f) && !(CONCAT_LINE(__breakFlag) = false); \
				CONCAT_LINE(__f) = false, CONCAT_LINE(__breakFlag) = true)

调试macro的时候可以用gcc的-E选项,他会只预处理,不进行编译,并把结果输出。


How it works?

首先我们要明白,要一般loop一个map的循环大概是下面这样写的:
for (map<string, int>::iterator it = m.begin(); it != m.end(); ++it) {
    const string& key = it->first;
    const int value = it->second;

    // .... 
}

而有了BOOST_AUTO和BOOST_FOREACH之后,我们可以简化成下面的样子:
BOOST_FOREACH(const pair<string, int>& p, m)
{
    BOOST_AUTO(key, p.first);
    BOOST_AUTO(value, p.second);
}

而我希望有的MAP_FOREACH的usage是下面这样:
MAP_FOREACH(key, value, m)
{
    // cout << key << value << endl;
}
类似于Python中的for ... in ...:
for key, value in m.iteritems():
     # loop

这样用起来就会方便很多。

首先知道for的语法如下:
for (declaraction ; condition; post-statements)
    // ....

最初的想法是像下面这样:
for (BOOST_AUTO(i, m.begin()), BOOST_AUTO(key, i->first), 
        BOOST_AUTO(value, i->second);
    i != m.end(); ++i)
{
    // ...
}
当然这个想法很天真,因为上面有两个地方是错了。
  1. 编译不过,一个for的声明语句部分是不能含有多个不同类型的变量的声明的。
  2. 声明部分是只有在进入for的时候才会执行一次的,所以在下次循环的时候不会重新赋值。
那么如果要实现对应的声明key和value,又要让这个定义能够像普通的for那样用,有什么办法?
首先,我可以用多个for的嵌套来定义其他的变量。为什么不用if或者while呢?虽然if和while可以用下面的用法:
if (int i = 0)
   int j = i;

while (int i = 0)
   int j = i;
但是需要注意,除了int和其他能够转化成bool的基本类型之外,是不可以在控制头里面写声明的。

所以就需要用嵌套的for来定义key和value了。就像下面这样:
#define MAP_FOREACH(key, value, m) \
for (BOOST_AUTO(i, m.begin()); i != m.end(); ++i) \
    for (BOOST_AUTO(key, i->first); /* condition */; ) \
        for (BOOST_AUTO(value, i->second); /* condition */; )
因为我们要使得MAP_FOREACH能够正常的工作,所以要让它里面两个循环都只循环一次。什么条件才可以让它只循环一次呢?
这里我使用了一个flag,使得在进入里面两层循环的时候,在判断条件的地方为true,在退出之后再次判断的时候为false。如下:
#define MAP_FOREACH(key, value, m) \
bool flag = true; \
for (BOOST_AUTO(i, m.begin()); i != m.end(); ++i) \
    for (BOOST_AUTO(key, i->first); flag; flag = false) \
        for (BOOST_AUTO(value, i->second); flag; flag = false)

如果单纯让上面的bool定义在外面的话,那么如果外面在用MAP_FOREACH之前就有一个flag变量,那么就会编译出错。所以我将flag也用for来声明。如下:
#define MAP_FOREACH(key, value, m) \
for (bool flag = true; flag; flag = false) \
    for (BOOST_AUTO(i, m.begin()); i != m.end(); ++i) \
        for (BOOST_AUTO(key, i->first); flag; flag = false) \
            for (BOOST_AUTO(value, i->second); flag; flag = false)

到这里,这个MAP_FOREACH已经和普通的for那样使用了,也可以和continue一起用。但是如果和break一起用的话,就会出现问题了。

如果我们像下面这样写:
MAP_FOREACH(key, value, m)
  break;
宏拓展后就是下面这样:
for (bool flag = true; flag; flag = false) 
    for (BOOST_AUTO(i, m.begin()); i != m.end(); ++i) 
        for (BOOST_AUTO(key, i->first); flag; flag = false) 
            for (BOOST_AUTO(value, i->second); flag; flag = false)
                break;
所以实际上跳出来的只是最里面的for,而并没有跳出整个MAP_FOREACH,那么就要想办法在跳出最里面的那层loop的时候,也跳出整个loop。
这里用的办法是再利用一个flag,在进入最里层loop的时候为false,在出来的时候设置为true。而如果用了break,在出来的语句就不会执行,导致跳出整个MAP_FOREACH,如下:
for (bool flag = true, breakFlag = true; flag; flag = false) 
    for (BOOST_AUTO(i, m.begin()); breakFlag && i != m.end(); ++i) 
        for (BOOST_AUTO(key, i->first); flag; flag = false) 
            for (BOOST_AUTO(value, i->second); flag && !(breakFlag = false); flag = false, breakFlag = true)
就像这样,就可以处理break语句了。

最后,由于将warning级别开到最大的话,不允许在condition的部分使用赋值语句,所以最后的版本将bool的flag改成了int类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值