mongo原始碼學習(四)invariant
前言
在看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> #defineMACRO_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(5) // output is 15 MACRO_ADD_NUMBERS(3,6) // 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 << "MACRO_ADD_NUMBERS(1) = " << MACRO_ADD_NUMBERS(1) << std::endl; std::cout << "MACRO_ADD_NUMBERS(1, 2) = " << MACRO_ADD_NUMBERS(1, 2) << std::endl; return 0; } 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&&,我們繼續哦。
C++中的#和##
C++中的&和&&