将do-while循环压缩到#define宏

时间:2013-12-29 07:55:14

标签: c++ c macros metaprogramming c-preprocessor

考虑以下示例代码(我实际上使用较长的二进制字符串,但这足以解释问题):

  void enumerateAllSubsets(unsigned char d) {
       unsigned char n = 0;
       do {
           cout<<binaryPrint(n)<<",";
       } while ( n = (n - d) & d );
    }

该函数(由于Knuth)有效地循环遍历二进制字符串的所有子集;

例如:

33 = '00100001' in binary and enumerateAllSubsets(33) would produce:

00000000, 00100000, 00000001, 00100001.

我需要写一个#define来制作

   macroEnumerate(n,33)
        cout<<binaryPrint(n)<<",";

的行为方式与enumerateAllSubsets(33)相当。 (好吧,订单可能会重新排列)

基本上我需要能够对一组子集执行各种操作。

使用for-loops做类似的事情是微不足道的:

for(int i=0;i < a.size();i++)
    foo(a[i]);

可以替换为:

 #define foreach(index,container) for(int index=0;index < container.size();index++)
    ...
    foreach(i,a)
        foo(a[i]);

enumerateAllSubsets()的问题是循环体需要无条件地执行一次,因此do-while不能被重写为。

我知道问题可以通过STL样式的模板化函数和传递给它的lambda来解决(类似于STL for_each函数),但是一些badass #define宏似乎是一个更清晰的解决方案。

4 个答案:

答案 0 :(得分:2)

一种选择是使用始终至少运行一次的for循环,例如:

for (bool once = true; once? (once = false, true) : (n = (n - d) & d); )
    // loop body

在第一次迭代中,一次变量被清除,表达式的计算结果为true,因此循环执行。从那时起,实际的测试和步骤逻辑控制循环。

从这里开始,将其重写为宏应该会容易得多。

希望这有帮助!

答案 1 :(得分:2)

假设C ++ 11,定义一个范围对象:

#include <iostream>
#include <iterator>
#include <cstdlib>

template <typename T>
class Subsets {
public:
    Subsets(T d, T n = 0)   : d_(d), n_(n) { }
    Subsets begin() const { return *this; }
    Subsets end() const { return {0, 0}; }

    bool operator!=(Subsets const & i) const { return d_ != i.d_ || n_ != i.n_; }

    Subsets & operator++() {
        if (!(n_ = (n_ - d_) & d_)) d_ = 0;
        return *this;
    }

    T operator*() const { return n_; }

private:
    T d_, n_;
};

template <typename T>
inline Subsets<T> make_subsets(T t) { return Subsets<T>(t); }

int main(int /*argc*/, char * argv[]) {
    int d = atoi(argv[1]);
    for (auto i : make_subsets(d))
        std::cout << i << "\n";
}

如果你想使用它,我已经非常通用,例如,uint64_t

答案 2 :(得分:0)

您可以使用表达式执行多行宏,如下所示:

#define macroenum(n, d, expr ) \
    n = 0;                     \
    do {                       \
        (expr);                \
    } while (n = (n -d) & d)   \
        ;                      \

int main(int argc, const char* argv[])
{

    enumerateAllSubsets(33);

    int n;
    macroenum(n, 33, cout << n << ",");
}

正如其他人所提到的,许多人认为这不会很干净 - 除其他外,它依赖于范围内存在的变量'n'。您可能需要将expr包装在另一组parens中,但我使用g ++测试它并获得与enumerateAllSubsets相同的输出。

答案 3 :(得分:0)

您的目标似乎是能够执行enumerateAllSubsets之类的操作,但更改为每次迭代执行的操作。

在C ++中,您可以使用头文件中的函数执行此操作:

template<typename Func>
inline void enumerateAllSubsets(unsigned char d, Func f)
{
   unsigned char n = 0;
   do { f(n); } while ( n = (n - d) & d );
}    

样本用法:

enumerateAllSubsets(33, [](auto n) { cout << binaryPrint(n) << ','; } );