mongo源码学习(四)invariant

时间:2022-01-26 13:36:44

前言

在看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&&,我们继续哦。

C++中的#和##

C++中的&和&&