前言
在看MongoDB源码的时候,经常会看到这个玩意儿:invariant。
invariant的字面意思是:不变式。
在emacs上跳转到函数定义要安装一个插件,ggtags,费了老大劲儿。这都可以重开一篇写一下了。
invariant的定义如下:
定义真的是恶心啊。。。
BOOST_PP_OVERLOAD
在看invariant的定义之前,先要了解一下:BOOST_PP_OVERLOAD
The BOOST_PP_OVERLOAD variadic macro expands to the name of a non-variadic macro having a given number of parameters.
Usage
BOOST_PP_OVERLOAD(prefix,...) (v)
Arguments
prefix
The prefix of the non-variadic macro name.
...
Variadic data. The number of variadic data elements, as determined by BOOST_PP_VARIADIC_SIZE, is appended to the prefix to form the output non-variadic macro name.
Remarks
This macro creates a macro name which depends on the number of elements of variadic data. It should be used in the form of
BOOST_PP_OVERLOAD(MACRO_NAME_,__VA_ARGS__)(__VA_ARGS__) in order to call a non-variadic macro taking a given number of variadic data elements as non-variadic arguments. In this way one can invoke a variadic macro with a variable number of parameters which calls one of a series of non-variadic macros doing very similar things.
Requirements
Header: <boost/preprocessor/facilities/overload.hpp>
Sample Code
#include <boost/preprocessor/facilities/overload.hpp> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/facilities/empty.hpp> #include <boost/preprocessor/arithmetic/add.hpp> #define MACRO_1(number) MACRO_2(number,10) #define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2) #define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__) // or for Visual C++ #define MACRO_ADD_NUMBERS(...) \ BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) MACRO_ADD_NUMBERS() // output is 15 MACRO_ADD_NUMBERS(,) // output is 9
BOOST_PP_OVERLOAD试一个可变参数的宏,用来扩展固定参数个数的非可变参数宏。
使用方法
BOOST_PP_OVERLOAD(prefix,...) (v)
prefix:非可变参数宏名称的前缀
...:可变的数据。可变数据元素的个数用BOOST_PP_VARIDIC_SIZE决定,通过prefix和BOOST_PP_VARIDIC_SIZE拼接出非可变参数宏的名称。
说明
这个宏可以根据可变数据的个数来生成一个宏的名称。为了对可变长度的参数调用一个非可变参数的宏,应该使用BOOST_PP_OVERLOAD(MACRO_NAME_,__VA_ARGS__)(__VA_ARGS__)这种形式。
举例
仿照Sample Code,我自己来写一个demo。
#include <iostream> #include <boost/preprocessor/facilities/overload.hpp> int add(int number1, int number2); #define MACRO_1(number) MACRO_2(number, 10) // mmp, 它会先检查add的原型, 然后再去做替换, 看来这里还是 #define MACRO_2(number1, number2) add(number1, number2) // 多参数的宏展开实现重载 #define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_, __VA_ARGS__)(__VA_ARGS__) int main() { std::cout << ) << std::endl; std::cout << , ) << std::endl; ; } int add(int number1, int number2) { return number1 + number2; }
对此,我只想说,你们真牛逼,用宏都可以实现重载。
这个是我第一次用到boost库,以后应该会有机会经常用吧,装这个狗玩意儿花了不少时间~
mongo中的invariant
接下来继续看mongo里面的invariant。
#pragma once #include <boost/preprocessor/facilities/overload.hpp> #include <string> #include "mongo/platform/compiler.h" #include "mongo/util/debug_util.h" namespace mongo { /** * This include exists so that mongo/base/status_with.h can use the invariant macro without causing * a circular include chain. It should never be included directly in any other file other than that * one (and assert_util.h). */ // 我擦, 这个defined还可以这么用 #if !defined(MONGO_INCLUDE_INVARIANT_H_WHITELISTED) #error "Include assert_util.h instead of invariant.h." #endif // 如果invariant failed会怎么办 MONGO_COMPILER_NORETURN void invariantFailed(const char* expr, const char* file, unsigned line) noexcept; // This overload is our legacy invariant, which just takes a condition to test. // // ex) invariant(!condition); // // Invariant failure !condition some/file.cpp 528 // // 这个重载是为了测试我们的条件, 比如invariant(!condition), 如果condition不成立的话 // 就会打印这个文件名和对应的行数, 相当于日志功能? // MONGO_invariant_是宏前缀, 1表示只有一个参数 // 他妈的, #Expression是个鸡毛意思哦 #define MONGO_invariant_1(Expression) \ ::mongo::invariantWithLocation((Expression), #Expression, __FILE__, __LINE__) // 模板来了, 模板只能在头文件中使用哦, 内联函数的模板 template <typename T> inline void invariantWithLocation(const T& testOK, const char* expr, const char* file, unsigned line) { if (MONGO_unlikely(!testOK)) { // 如果测试不合格就执行下面的函数了 ::mongo::invariantFailed(expr, file, line); } } // 同样也是failed的情况 MONGO_COMPILER_NORETURN void invariantFailedWithMsg(const char* expr, const std::string& msg, const char* file, unsigned line) noexcept; // This invariant overload accepts a condition and a message, to be logged if the condition is // false. // // ex) invariant(!condition, "hello!"); // // Invariant failure !condition "hello!" some/file.cpp 528 // // 附加了一个信息 #define MONGO_invariant_2(Expression, contextExpr) \ ::mongo::invariantWithContextAndLocation((Expression), \ #Expression, \ [&]() -> std::string { return (contextExpr); }, \ __FILE__, \ __LINE__) // 又是一个模板 template <typename T, typename ContextExpr> inline void invariantWithContextAndLocation( const T& testOK, const char* expr, ContextExpr&& contextExpr, const char* file, unsigned line) { if (MONGO_unlikely(!testOK)) { ::mongo::invariantFailedWithMsg(expr, contextExpr(), file, line); } } // This helper macro is necessary to make the __VAR_ARGS__ expansion work properly on MSVC. // 这里还要注意在Microsoft Visual C++里面的坑, 所以专门用了这么个宏 #define MONGO_expand(x) x // 实现宏重载 #define invariant(...) \ MONGO_expand(MONGO_expand(BOOST_PP_OVERLOAD(MONGO_invariant_, __VA_ARGS__))(__VA_ARGS__)) // Behaves like invariant in debug builds and is compiled out in release. Use for checks, which can // potentially be slow or on a critical path. // 又来了一个assert #define MONGO_dassert(...) \ if (kDebugBuild) \ invariant(__VA_ARGS__) #define dassert MONGO_dassert } // namespace mongo
反正我是不怕invariant了,我知道这个要干嘛了,哈哈,好多地方都用到了这个玩意儿。其实就是判断一个表达式是否成立,然后打印一下语句,包括文件名和行号这些。
但其实还有几个东西又不懂了,比如#Expression,ContextExpr&&,我们继续哦。