1. 程式人生 > >mongo原始碼學習(四)invariant

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>

#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(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++中的&和&&