无限宏展开技巧和 Assert 扩展

本文来自这篇 翻译 (原文),这个宏技巧的发明人是 Paul Mensonides。

直接上代码:

#define _A(x) _OP(x,B)
#define _B(x) _OP(x,A)
#define _OP(x,next) f(x)->_##next

_A(1)(2)(3)(4) // f(1)->f(2)->f(3)->f(4)->_A
_A(1)(2)(3)(4)(5) // f(1)->f(2)->f(3)->f(4)->f(5)->_B

简单地理解,预处理器在看到 _A(...) 时会进行展开,而看到 _A 时则什么都不做。通过将 _A, _B 实现为成员变量,再实现链式调用,就可以实现下面这个 Assert 扩展。

struct Assert {
    const char *expr = nullptr;
    Assert& SMART_ASSERT_A = *this;
    Assert& SMART_ASSERT_B = *this;
    Assert() = default;
    Assert& ctx(const char* file, int line) {
        printf("Assertion failed in %s: %d\n", file, line);
        printf("Expression: '%s'\n", expr);
        return *this;
    }
    template <typename T>
    Assert& val(T x, const char* name) {
        printf("Value %s = ", name);
        cout << x << endl;
        return *this;
    }
};

#define SMART_ASSERT_A(x) SMART_ASSERT_OP(x, B)
#define SMART_ASSERT_B(x) SMART_ASSERT_OP(x, A)
#define SMART_ASSERT_OP(x, next) \
    SMART_ASSERT_A.val((x), #x).SMART_ASSERT_##next
#define SMART_ASSERT(expr) \
    if (!(expr)) (Assert { #expr }).ctx(__FILE__, __LINE__).SMART_ASSERT_A

int main() {
    string a = "not empty";
    string b = "";
    SMART_ASSERT(a.empty() && b.empty())(a)(b);
/* Assertion failed in a.cpp: 34
 * Expression: 'a.empty() && b.empty()'
 * Value a = not empty
 * Value b =
 */
}

扩展阅读: map-macromacrofun

其实不用上面的魔法,Assert 也可以写得更一般:

#define assrt(e) if (!(e)) assrt(#e, e)
#define val(a) val(#a, a)
assrt(a + b).val(a).val(b).done();
/* if (!(a + b)) assrt("a + b", a + b).val("a", a).val("b", b).done(); */