IKFOM代码解析

Catalogue
  1. 1. Kalman Filters on Differentiable Manifolds
  2. 2. IkFoM在Fast-LIO2中的应用
    1. 2.1. MTK_BUILD_MANIFOLD干了啥
      1. 2.1.1. 【方法一】使用IDE展开宏定义
      2. 2.1.2. 【方法二】编写代码展开宏定义并打印
    2. 2.2. MTK_BUILD_MANIFOLD细读
      1. 2.2.1. MTK_SUBVARLIST
  3. 3. 附录
    1. 3.1. BOOST_PP_FOR
    2. 3.2. BOOST_PP运算
    3. 3.3. BOOST_PP序列
    4. 3.4. BOOST_PP_IF(cond, t, f)

Kalman Filters on Differentiable Manifolds

IkFoM在Fast-LIO2中的应用

Fast-LIO2中,主要使用了IkFoM作为状态,其中,在use-ikfom.hpp声明了关于各种状态、数据的宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <IKFoM_toolkit/esekfom/esekfom.hpp>

typedef MTK::vect<3, double> vect3;
typedef MTK::SO3<double> SO3;
typedef MTK::S2<double, 98090, 10000, 1> S2;
typedef MTK::vect<1, double> vect1;
typedef MTK::vect<2, double> vect2;

MTK_BUILD_MANIFOLD(state_ikfom,
((vect3, pos))
((SO3, rot))
((SO3, offset_R_L_I))
((vect3, offset_T_L_I))
((vect3, vel))
((vect3, bg))
((vect3, ba))
((S2, grav))
);

MTK_BUILD_MANIFOLD(input_ikfom,
((vect3, acc))
((vect3, gyro))
);

MTK_BUILD_MANIFOLD(process_noise_ikfom,
((vect3, ng))
((vect3, na))
((vect3, nbg))
((vect3, nba))
);

...

其中,MTK_BUILD_MANIFOLD宏定义是引用自build_manifold.hpp文件,即mtk文件夹内的内容,mtk文件夹原版是来自(OpenSLAM-MTK)[https://github.com/OpenSLAM-org/openslam_MTK],并且经过了一定的修改和适配。

MTK_BUILD_MANIFOLD干了啥

MTK_BUILD_MANIFOLD是由BOOST宏模板编程技术写的宏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef MTK_AUTOCONSTRUCT_HPP_
#define MTK_AUTOCONSTRUCT_HPP_

#include <vector>

#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/cat.hpp>
#include <Eigen/Core>

#include "src/SubManifold.hpp"
#include "startIdx.hpp"

#ifndef PARSED_BY_DOXYGEN
//////// internals //////

#define MTK_APPLY_MACRO_ON_TUPLE(r, macro, tuple) macro tuple

#define MTK_TRANSFORM_COMMA(macro, entries) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM_S(1, MTK_APPLY_MACRO_ON_TUPLE, macro, entries))

#define MTK_TRANSFORM(macro, entries) BOOST_PP_SEQ_FOR_EACH_R(1, MTK_APPLY_MACRO_ON_TUPLE, macro, entries)

#define MTK_CONSTRUCTOR_ARG( type, id) const type& id = type()
#define MTK_CONSTRUCTOR_COPY( type, id) id(id)
#define MTK_BOXPLUS( type, id) id.boxplus(MTK::subvector(__vec, &self::id), __scale);
#define MTK_OPLUS( type, id) id.oplus(MTK::subvector_(__vec, &self::id), __scale);

...
  • #ifndef PARSED_BY_DOXYGEN 表示接下来的代码,将不是由 DOXYGEN 工具来解析,而是由C++预处理来处理
  • build_manifold.hpp文件使用了大量的Boost库的预处理(PP: preprocessor)模板元库技术,直接看代码比较复杂,所以这里反过来看,即直接对宏展开,然后往回看。

【方法一】使用IDE展开宏定义

input_ikfom的定义代码块为例:

1
2
3
4
MTK_BUILD_MANIFOLD(input_ikfom,
((vect3, acc))
((vect3, gyro))
);

直接使用CLion展开是最快的,把鼠标移到附近,等待解析完成,显示如下:

展开结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
struct input_ikfom {
typedef input_ikfom self;
std::vector<std::pair<int, int>> S2_state;
std::vector<std::pair<int, int>> SO3_state;
std::vector<std::pair<std::pair<int, int>, int>> vect_state;
MTK::SubManifold<vect3, 0, 0> acc;
MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro;
enum {
DOF = vect3::DOF + 0 + vect3::DOF
};
enum {
DIM = vect3::DIM + 0 + vect3::DIM
};
typedef vect3::scalar scalar;
input_ikfom(const vect3 &acc = vect3(), const vect3 &gyro = vect3()) : acc(acc), gyro(gyro) {}
int getDOF() const { return DOF; }
void boxplus(const MTK::vectview<const scalar, DOF> &__vec, scalar __scale = 1) {
acc.boxplus(MTK::subvector(__vec, &self::acc), __scale);
gyro.boxplus(MTK::subvector(__vec, &self::gyro), __scale);
}
void oplus(const MTK::vectview<const scalar, DIM> &__vec, scalar __scale = 1) {
acc.oplus(MTK::subvector_(__vec, &self::acc), __scale);
gyro.oplus(MTK::subvector_(__vec, &self::gyro), __scale);
}
void boxminus(MTK::vectview<scalar, DOF> __res, const input_ikfom &__oth) const {
acc.boxminus(MTK::subvector(__res, &self::acc), __oth.acc);
gyro.boxminus(MTK::subvector(__res, &self::gyro), __oth.gyro);
}
friend std::ostream &operator<<(std::ostream &__os, const input_ikfom &__var) {
return __os << __var.acc << " " << __var.gyro << " ";
}
void build_S2_state() {
if (acc.TYP == 1) { S2_state.push_back(std::make_pair(acc.IDX, acc.DIM)); }
if (gyro.TYP == 1) { S2_state.push_back(std::make_pair(gyro.IDX, gyro.DIM)); }
}
void build_vect_state() {
if (acc.TYP == 0) {
(vect_state).push_back(std::make_pair(std::make_pair(acc.IDX, acc.DIM), vect3::DOF));
}
if (gyro.TYP == 0) { (vect_state).push_back(std::make_pair(std::make_pair(gyro.IDX, gyro.DIM), vect3::DOF)); }
}
void build_SO3_state() {
if (acc.TYP == 2) { (SO3_state).push_back(std::make_pair(acc.IDX, acc.DIM)); }
if (gyro.TYP == 2) { (SO3_state).push_back(std::make_pair(gyro.IDX, gyro.DIM)); }
}
void S2_hat(Eigen::Matrix<scalar, 3, 3> &res, int idx) {
if (acc.IDX == idx) { acc.S2_hat(res); }
if (gyro.IDX == idx) { gyro.S2_hat(res); }
}
void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3> &res, int idx) {
if (acc.IDX == idx) { acc.S2_Nx_yy(res); }
if (gyro.IDX == idx) { gyro.S2_Nx_yy(res); }
}
void S2_Mx(Eigen::Matrix<scalar, 3, 2> &res, Eigen::Matrix<scalar, 2, 1> dx, int idx) {
if (acc.IDX == idx) {
acc.S2_Mx(res, dx);
}
if (gyro.IDX == idx) { gyro.S2_Mx(res, dx); }
}
friend std::istream &operator>>(std::istream &__is, input_ikfom &__var) { return __is >> __var.acc >> __var.gyro; }
};

【方法二】编写代码展开宏定义并打印

新建test文件夹,添加test_ikfom.cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

#include "../include/IKFoM_toolkit/esekfom/esekfom.hpp"


typedef MTK::vect<3, double> vect3;
typedef MTK::SO3<double> SO3;
typedef MTK::S2<double, 98090, 10000, 1> S2;
typedef MTK::vect<1, double> vect1;
typedef MTK::vect<2, double> vect2;


#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__

#pragma message "Marco is : " \
STR(MTK_BUILD_MANIFOLD(input_ikfom, \
((vect3, acc)) \
((vect3, gyro)) \
)\
)

int main() {
printf("%s\n",
STR(MTK_BUILD_MANIFOLD(input_ikfom, \
((vect3, acc)) \
((vect3, gyro)) \
)\
)
);
return 0;
}

其中,__VA_ARGS__是可变参数宏标识符,#define STR(...)里面的(...)表示可以传递多个参数。

需要注意的是:

  • ​​#运算符​​ 把参数字符串化,功能就是转为字符串

为什么需要用两个STR宏,是因为:

  • STR(...)MTK_BUILD_MANIFOLD(xxxx)这一串字符传递给__VA_ARGS__变量,如果此时就调用#__VA_ARGS__进行打印,那么输出的仍然是MTK_BUILD_MANIFOLD(xxxx),为了展开,需要将__VA_ARGS__的值(也就是MTK_BUILD_MANIFOLD(xxxx)的值)进行传递,所以有了STR_(...),此时MTK_BUILD_MANIFOLD(xxxx)的具体值就会传递给STR_(...)__VA_ARGS__,这个时候再打印,就是完整的宏定义展开了。

运行此CPP,得到输出结果如下:

1
struct input_ikfom { typedef input_ikfom self; std::vector<std::pair<int, int> > S2_state; std::vector<std::pair<int, int> > SO3_state; std::vector<std::pair<std::pair<int, int>, int> > vect_state; MTK::SubManifold<vect3, 0, 0> acc; MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro; enum {DOF = vect3::DOF + 0 + vect3::DOF}; enum {DIM = vect3::DIM+0 + vect3::DIM}; typedef vect3::scalar scalar; input_ikfom ( const vect3& acc = vect3(), const vect3& gyro = vect3() ) : acc(acc), gyro(gyro) {} int getDOF() const { return DOF; } void boxplus(const MTK::vectview<const scalar, DOF> & __vec, scalar __scale = 1 ) { acc.boxplus(MTK::subvector(__vec, &self::acc), __scale); gyro.boxplus(MTK::subvector(__vec, &self::gyro), __scale); } void oplus(const MTK::vectview<const scalar, DIM> & __vec, scalar __scale = 1 ) { acc.oplus(MTK::subvector_(__vec, &self::acc), __scale); gyro.oplus(MTK::subvector_(__vec, &self::gyro), __scale); } void boxminus(MTK::vectview<scalar,DOF> __res, const input_ikfom& __oth) const { acc.boxminus(MTK::subvector(__res, &self::acc), __oth.acc); gyro.boxminus(MTK::subvector(__res, &self::gyro), __oth.gyro); } friend std::ostream& operator<<(std::ostream& __os, const input_ikfom& __var){ return __os << __var.acc << " " << __var.gyro << " " ; } void build_S2_state(){ if(acc.TYP == 1){S2_state.push_back(std::make_pair(acc.IDX, acc.DIM));} if(gyro.TYP == 1){S2_state.push_back(std::make_pair(gyro.IDX, gyro.DIM));} } void build_vect_state(){ if(acc.TYP == 0){(vect_state).push_back(std::make_pair(std::make_pair(acc.IDX, acc.DIM), vect3::DOF));} if(gyro.TYP == 0){(vect_state).push_back(std::make_pair(std::make_pair(gyro.IDX, gyro.DIM), vect3::DOF));} } void build_SO3_state(){ if(acc.TYP == 2){(SO3_state).push_back(std::make_pair(acc.IDX, acc.DIM));} if(gyro.TYP == 2){(SO3_state).push_back(std::make_pair(gyro.IDX, gyro.DIM));} } void S2_hat(Eigen::Matrix<scalar, 3, 3> &res, int idx) { if(acc.IDX == idx){acc.S2_hat(res);} if(gyro.IDX == idx){gyro.S2_hat(res);} } void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3> &res, int idx) { if(acc.IDX == idx){acc.S2_Nx_yy(res);} if(gyro.IDX == idx){gyro.S2_Nx_yy(res);} } void S2_Mx(Eigen::Matrix<scalar, 3, 2> &res, Eigen::Matrix<scalar, 2, 1> dx, int idx) { if(acc.IDX == idx){acc.S2_Mx(res, dx);} if(gyro.IDX == idx){gyro.S2_Mx(res, dx);} } friend std::istream& operator>>(std::istream& __is, input_ikfom& __var){ return __is >> __var.acc >> __var.gyro ; } };

重新格式化后,得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
struct input_ikfom {
typedef input_ikfom self;
std::vector<std::pair<int, int> > S2_state;
std::vector<std::pair<int, int> > SO3_state;
std::vector<std::pair<std::pair<int, int>, int> > vect_state;
MTK::SubManifold<vect3, 0, 0> acc;
MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro;
enum {
DOF = vect3::DOF + 0 + vect3::DOF
};
enum {
DIM = vect3::DIM + 0 + vect3::DIM
};
typedef vect3::scalar scalar;
input_ikfom(const vect3 &acc = vect3(), const vect3 &gyro = vect3()) : acc(acc), gyro(gyro) {}
int getDOF() const { return DOF; }
void boxplus(const MTK::vectview<const scalar, DOF> &__vec, scalar __scale = 1) {
acc.boxplus(MTK::subvector(__vec, &self::acc), __scale);
gyro.boxplus(MTK::subvector(__vec, &self::gyro), __scale);
}
void oplus(const MTK::vectview<const scalar, DIM> &__vec, scalar __scale = 1) {
acc.oplus(MTK::subvector_(__vec, &self::acc), __scale);
gyro.oplus(MTK::subvector_(__vec, &self::gyro), __scale);
}
void boxminus(MTK::vectview<scalar, DOF> __res, const input_ikfom &__oth) const {
acc.boxminus(MTK::subvector(__res, &self::acc), __oth.acc);
gyro.boxminus(MTK::subvector(__res, &self::gyro), __oth.gyro);
}
friend std::ostream &operator<<(std::ostream &__os, const input_ikfom &__var) {
return __os << __var.acc << " " << __var.gyro << " ";
}
void build_S2_state() {
if (acc.TYP == 1) { S2_state.push_back(std::make_pair(acc.IDX, acc.DIM)); }
if (gyro.TYP == 1) { S2_state.push_back(std::make_pair(gyro.IDX, gyro.DIM)); }
}
void build_vect_state() {
if (acc.TYP == 0) {
(vect_state).push_back(std::make_pair(std::make_pair(acc.IDX, acc.DIM), vect3::DOF));
}
if (gyro.TYP == 0) { (vect_state).push_back(std::make_pair(std::make_pair(gyro.IDX, gyro.DIM), vect3::DOF)); }
}
void build_SO3_state() {
if (acc.TYP == 2) { (SO3_state).push_back(std::make_pair(acc.IDX, acc.DIM)); }
if (gyro.TYP == 2) { (SO3_state).push_back(std::make_pair(gyro.IDX, gyro.DIM)); }
}
void S2_hat(Eigen::Matrix<scalar, 3, 3> &res, int idx) {
if (acc.IDX == idx) { acc.S2_hat(res); }
if (gyro.IDX == idx) { gyro.S2_hat(res); }
}
void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3> &res, int idx) {
if (acc.IDX == idx) { acc.S2_Nx_yy(res); }
if (gyro.IDX == idx) { gyro.S2_Nx_yy(res); }
}
void S2_Mx(Eigen::Matrix<scalar, 3, 2> &res, Eigen::Matrix<scalar, 2, 1> dx, int idx) {
if (acc.IDX == idx) {
acc.S2_Mx(res, dx);
}
if (gyro.IDX == idx) { gyro.S2_Mx(res, dx); }
}
friend std::istream &operator>>(std::istream &__is, input_ikfom &__var) { return __is >> __var.acc >> __var.gyro; }
};

两种方法得到的结果,是一致的

MTK_BUILD_MANIFOLD细读

有了输入、输出,这个时候结合MTK_BUILD_MANIFOLD的代码来分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#define MTK_BUILD_MANIFOLD(name, entries) \
struct name { \
typedef name self; \
std::vector<std::pair<int, int> > S2_state;\
std::vector<std::pair<int, int> > SO3_state;\
std::vector<std::pair<std::pair<int, int>, int> > vect_state;\
MTK_SUBVARLIST(entries, S2_state, SO3_state) \
name ( \
MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_ARG, entries) \
) : \
MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_COPY, entries) {}\
int getDOF() const { return DOF; } \
void boxplus(const MTK::vectview<const scalar, DOF> & __vec, scalar __scale = 1 ) { \
MTK_TRANSFORM(MTK_BOXPLUS, entries)\
} \
void oplus(const MTK::vectview<const scalar, DIM> & __vec, scalar __scale = 1 ) { \
MTK_TRANSFORM(MTK_OPLUS, entries)\
} \
void boxminus(MTK::vectview<scalar,DOF> __res, const name& __oth) const { \
MTK_TRANSFORM(MTK_BOXMINUS, entries)\
} \
friend std::ostream& operator<<(std::ostream& __os, const name& __var){ \
return __os MTK_TRANSFORM(MTK_OSTREAM, entries); \
} \
void build_S2_state(){\
MTK_TRANSFORM(MTK_S2_state, entries)\
}\
void build_vect_state(){\
MTK_TRANSFORM(MTK_vect_state, entries)\
}\
void build_SO3_state(){\
MTK_TRANSFORM(MTK_SO3_state, entries)\
}\
void S2_hat(Eigen::Matrix<scalar, 3, 3> &res, int idx) {\
MTK_TRANSFORM(MTK_S2_hat, entries)\
}\
void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3> &res, int idx) {\
MTK_TRANSFORM(MTK_S2_Nx_yy, entries)\
}\
void S2_Mx(Eigen::Matrix<scalar, 3, 2> &res, Eigen::Matrix<scalar, 2, 1> dx, int idx) {\
MTK_TRANSFORM(MTK_S2_Mx, entries)\
}\
friend std::istream& operator>>(std::istream& __is, name& __var){ \
return __is MTK_TRANSFORM(MTK_ISTREAM, entries); \
} \
};

name = input_ikfom代入上面,得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#define MTK_BUILD_MANIFOLD(name, entries) \
struct input_ikfom { \
typedef input_ikfom self; \
std::vector<std::pair<int, int> > S2_state;\
std::vector<std::pair<int, int> > SO3_state;\
std::vector<std::pair<std::pair<int, int>, int> > vect_state;\
MTK_SUBVARLIST(entries, S2_state, SO3_state) \
input_ikfom ( \
MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_ARG, entries) \
) : \
MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_COPY, entries) {}\
int getDOF() const { return DOF; } \
void boxplus(const MTK::vectview<const scalar, DOF> & __vec, scalar __scale = 1 ) { \
MTK_TRANSFORM(MTK_BOXPLUS, entries)\
} \
void oplus(const MTK::vectview<const scalar, DIM> & __vec, scalar __scale = 1 ) { \
MTK_TRANSFORM(MTK_OPLUS, entries)\
} \
void boxminus(MTK::vectview<scalar,DOF> __res, const input_ikfom& __oth) const { \
MTK_TRANSFORM(MTK_BOXMINUS, entries)\
} \
friend std::ostream& operator<<(std::ostream& __os, const input_ikfom& __var){ \
return __os MTK_TRANSFORM(MTK_OSTREAM, entries); \
} \
void build_S2_state(){\
MTK_TRANSFORM(MTK_S2_state, entries)\
}\
void build_vect_state(){\
MTK_TRANSFORM(MTK_vect_state, entries)\
}\
void build_SO3_state(){\
MTK_TRANSFORM(MTK_SO3_state, entries)\
}\
void S2_hat(Eigen::Matrix<scalar, 3, 3> &res, int idx) {\
MTK_TRANSFORM(MTK_S2_hat, entries)\
}\
void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3> &res, int idx) {\
MTK_TRANSFORM(MTK_S2_Nx_yy, entries)\
}\
void S2_Mx(Eigen::Matrix<scalar, 3, 2> &res, Eigen::Matrix<scalar, 2, 1> dx, int idx) {\
MTK_TRANSFORM(MTK_S2_Mx, entries)\
}\
friend std::istream& operator>>(std::istream& __is, input_ikfom& __var){ \
return __is MTK_TRANSFORM(MTK_ISTREAM, entries); \
} \
};

接下来,还有

  • MTK_SUBVARLIST,
  • MTK_TRANSFORM_COMMA,
  • MTK_TRANSFORM,
  • MTK_CONSTRUCTOR_ARG,
  • MTK_CONSTRUCTOR_COPY,
  • MTK_BOXPLUS,
  • MTK_OSTREAM,
  • MTK_S2_state,
  • MTK_vect_state,
  • MTK_SO3_state,
  • MTK_S2_hat,
  • MTK_S2_Nx_yy,
  • MTK_S2_Mx,
  • MTK_ISTREAM,
  • MTK::vectview

需要展开。

MTK_SUBVARLIST

MTK_SUBVARLIST的定义如下:

1
2
3
4
5
6
7
8
9
10
11
#define MTK_SUBVARLIST(seq, S2state, SO3state) \
BOOST_PP_FOR_1( \
( \
BOOST_PP_SEQ_SIZE(seq), \
BOOST_PP_SEQ_HEAD(seq), \
BOOST_PP_SEQ_TAIL(seq) (~), \
0,\
0,\
S2state,\
SO3state ),\
MTK_ENTRIES_TEST, MTK_ENTRIES_NEXT, MTK_ENTRIES_OUTPUT)

输入:

1
2
3
4
std::vector<std::pair<int, int> > S2_state;
std::vector<std::pair<int, int> > SO3_state;
std::vector<std::pair<std::pair<int, int>, int> > vect_state;
MTK_SUBVARLIST(((vect3, acc)) ((vect3, gyro)) , S2_state, SO3_state) \

输出:

1
2
3
4
5
6
7
8
9
10
11
12
std::vector<std::pair<int, int> > S2_state;
std::vector<std::pair<int, int> > SO3_state;
std::vector<std::pair<std::pair<int, int>, int> > vect_state;
MTK::SubManifold<vect3, 0, 0> acc;
MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro;
enum {
DOF = vect3::DOF + 0 + vect3::DOF
};
enum {
DIM = vect3::DIM + 0 + vect3::DIM
};
typedef vect3::scalar scalar;

此处用了BOOST_PP_FOR_1,根据附录,BOOST_PP_FOR(state, pred, op, macro) 宏用于执行一定数量的宏展开操作

在这里,

  • 循环变量的初始状态state为
1
2
3
4
5
6
7
8
( \
BOOST_PP_SEQ_SIZE(seq), \
BOOST_PP_SEQ_HEAD(seq), \
BOOST_PP_SEQ_TAIL(seq) (~), \
0,\
0,\
S2state,\
SO3state )
  • 结束循环的条件pred为MTK_ENTRIES_TEST
  • 产生新状态的操作op为MTK_ENTRIES_NEXT
  • 具体输出操作macro为MTK_ENTRIES_OUTPUT

============= 第一轮循环 ============

初始状态

原始输入:

1
2
seq =     ((vect3, acc))  \
((vect3, gyro)) \

输入初始状态:

1
2
3
4
5
6
7
8
( \
BOOST_PP_SEQ_SIZE(seq), \
BOOST_PP_SEQ_HEAD(seq), \
BOOST_PP_SEQ_TAIL(seq) (~), \
0,\
0,\
S2state,\
SO3state )
  • BOOST_PP_SEQ_SIZE(seq) = 2
  • BOOST_PP_SEQ_HEAD(seq) = (vect3, acc)
  • BOOST_PP_SEQ_TAIL(seq) (~) = ((vect3, gyro))

即初始状态为

1
2
3
4
5
6
7
8
9
( \
2, \
(vect3, acc), \
((vect3, gyro)), \
0,\
0,\
S2state,\
SO3state\
)

MTK_ENTRIES_TEST

取state第0个元素,非0则继续循环,为0则结束循环

1
2
3
4
5
//! this used to be BOOST_PP_TUPLE_ELEM_4_0:
#define MTK_TUPLE_ELEM_4_0(a,b,c,d,e,f, g) a

// 取state第0个元素
#define MTK_ENTRIES_TEST(r, state) MTK_TUPLE_ELEM_4_0 state

MTK_ENTRIES_OUTPUT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define MTK_APPLY_MACRO_ON_TUPLE(r, macro, tuple) macro tuple

#define MTK_PUT_TYPE(type, id, dof, dim, S2state, SO3state) \
MTK::SubManifold<type, dof, dim> id;
#define MTK_PUT_TYPE_AND_ENUM(type, id, dof, dim, S2state, SO3state) \
MTK_PUT_TYPE(type, id, dof, dim, S2state, SO3state) \
enum {DOF = type::DOF + dof}; \
enum {DIM = type::DIM+dim}; \
typedef type::scalar scalar;

#define MTK_ENTRIES_OUTPUT_I(s, head, seq, dof, dim, S2state, SO3state) \
MTK_APPLY_MACRO_ON_TUPLE(~, \
BOOST_PP_IF(BOOST_PP_DEC(s), MTK_PUT_TYPE, MTK_PUT_TYPE_AND_ENUM), \
( BOOST_PP_TUPLE_REM_2 head, dof, dim, S2state, SO3state))

// 主要调用
#define MTK_ENTRIES_OUTPUT(r, state) MTK_ENTRIES_OUTPUT_I state

初始状态为

1
2
3
4
5
6
7
8
9
( \
2, \
(vect3, acc), \
((vect3, gyro)), \
0,\
0,\
S2state,\
SO3state\
)

调用:

1
2
3
4
5
6
7
MTK_ENTRIES_OUTPUT_I (s = 2, head = (vect3, acc), seq = ((vect3, gyro)), dof = 0, dim = 0, S2state = S2state, SO3state = SO3state)

// MTK_ENTRIES_OUTPUT_I的内部流程为:
// 如果 s-1 > 0 :
// 那么,以(head[0], head[1], dof, dim, S2state, SO3state)为参数,调用MTK_PUT_TYPE宏
// 如果 s-1 <=0:
// 那么,以(head[0], head[1], dof, dim, S2state, SO3state)为参数,调用MTK_PUT_TYPE_AND_ENUM宏

此处,s - 1=2 - 1 > 0,所以会调用MTK_PUT_TYPE

1
2
#define MTK_PUT_TYPE(type, id, dof, dim, S2state, SO3state) \
MTK::SubManifold<type, dof, dim> id;

输出:

1
MTK::SubManifold<vect3, 0, 0> acc;

MTK_ENTRIES_NEXT

1
2
3
4
5
6
7
8
9
10
#define MTK_ENTRIES_NEXT(r, state) MTK_ENTRIES_NEXT_I state

#define MTK_ENTRIES_NEXT_I(len, head, seq, dof, dim, S2state, SO3state) ( \
BOOST_PP_DEC(len), \
BOOST_PP_SEQ_HEAD(seq), \
BOOST_PP_SEQ_TAIL(seq), \
dof + BOOST_PP_TUPLE_ELEM_2_0 head::DOF,\
dim + BOOST_PP_TUPLE_ELEM_2_0 head::DIM,\
S2state,\
SO3state)

初始状态为

1
2
3
4
5
6
7
8
9
( \
2, \
(vect3, acc), \
((vect3, gyro)), \
0,\
0,\
S2state,\
SO3state\
)

调用:

1
MTK_ENTRIES_NEXT_I(len = 2, head = (vect3, acc), seq = ((vect3, gyro)), 0, 0, S2state, SO3state)

经过op操作(MTK_ENTRIES_NEXT)之后,得到新的state:

1
2
3
4
5
6
7
1, \
(vect3, gyro), \
, \
0 + head[0]::DOF,\
0 + head[0]::DIM,\
S2state,\
SO3state\

============= 第二轮循环 ============

当前状态:

1
2
3
4
5
6
7
1, \
(vect3, gyro), \
, \
0 + head[0]::DOF,\
0 + head[0]::DIM,\
S2state,\
SO3state\

MTK_ENTRIES_TEST

第0个元素为1,继续执行

MTK_ENTRIES_OUTPUT

输出:

1
2
3
4
5
6
7
8
MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro;
enum {
DOF = vect3::DOF + 0 + vect3::DOF
};
enum {
DIM = vect3::DIM + 0 + vect3::DIM
};
typedef vect3::scalar scalar;

MTK_ENTRIES_NEXT

op操作,得到下一个状态:

1
(0,BOOST_PP_SEQ_ELEM_III,BOOST_PP_SEQ_TAIL_I,0+vect3::DOF+vect3::DOF,0+vect3::DIM+vect3::DIM,S2state,SO3state)

============= 结束循环 ============

最终,MTK_SUBVARLIST得到结果如下:

输出:

1
2
3
4
5
6
7
8
9
10
11
12
// 第一轮循环输出的
MTK::SubManifold<vect3, 0, 0> acc;

// 第二轮循环输出的
MTK::SubManifold<vect3, 0 + vect3::DOF, 0 + vect3::DIM> gyro;
enum {
DOF = vect3::DOF + 0 + vect3::DOF
};
enum {
DIM = vect3::DIM + 0 + vect3::DIM
};
typedef vect3::scalar scalar;

附录

BOOST_PP_FOR

BOOST_PP_FOR 是 Boost Preprocessor 库中的一个宏,用于实现基于循环的元编程。BOOST_PP_FOR(state, pred, op, macro) 表示泛化的for水平重复结构,它接受四个参数:

  • state : 初始状态
  • pred : 判断是否继续展开,形如 pred(r, state) 的二元谓词。该宏必须展开为一个位于 0 到 BOOST_PP_LIMIT_MAG 间的整数。当该谓词返回非零时,BOOST_PP_FOR 重复展开 macro
  • op : 操作生成新的状态,形如 op(r, state) 的二元操作; 该宏被重复应用于 state, 每次产生一个新的 state, 直至 pred 返回 0.
  • macro: 利用state生成做后的输出,形如 macro(r, state) 的二元宏;该宏被 BOOST_PP_FOR 重复调用,直至 pred 返回 0

简单来说,使用BOOST_PP_FOR,大概会展开成这个模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
macro(r, state) macro(r, op(r, state)) … macro(r, op(r, … op(r, state) … ))
``

常用的pred大概长这样:

```c++
// 当state的首元素不等于state第二元素+1时,返回1,否则返回0
#define PRED(r, state) \
BOOST_PP_NOT_EQUAL( \
BOOST_PP_TUPLE_ELEM(2, 0, state), \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 1, state)) \
)

// BOOST_PP_NOT_EQUAL , BOOST_PP_INC 可以看下面的BOOST_PP运算部分
// BOOST_PP_TUPLE_ELEM表示从state中取第0个元素

一个op操作举例如下:

1
2
3
4
5
6
7
8
#define OP(r, state) \
( \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 0, state)), \ // 递增首元素
BOOST_PP_TUPLE_ELEM(2, 1, state) \ // 取出第二元素 ,组合成新的元组(state)
)

// 输入: (5, 10) 则state = (5, 10)
// 输出: (6, 10)

一个macro操作举例如下:

1
2
3
4
5
6
7
#define MACRO(r, state) BOOST_PP_TUPLE_ELEM(2, 0, state) // 取元组的首元素

// 输入: (5, 10)
// 输出: 5

// 输入: (6, 10)
// 输出: 6

对上面的操作进行组合,形成一个BOOST_PP_FOR操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 // 当state的首元素不等于state第二元素+1时,返回1,否则返回0
#define PRED(r, state) \
BOOST_PP_NOT_EQUAL( \
BOOST_PP_TUPLE_ELEM(2, 0, state), \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 1, state)) \
)
#define OP(r, state) \
( \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 0, state)), \ // 递增首元素
BOOST_PP_TUPLE_ELEM(2, 1, state) \ // 取出第二元素 ,组合成新的元组(state)
)

// 取元组的首元素
#define MACRO(r, state) BOOST_PP_TUPLE_ELEM(2, 0, state)

// 展开为 5 6 7 8 9 10(当状态为(11,10)停止,不输出)
BOOST_PP_FOR((5, 10), PRED, OP, MACRO)

BOOST_PP运算

算术运算

1
2
3
4
5
6
7
BOOST_PP_ADD(x,y)          x + y
BOOST_PP_DEC(x) x - 1
BOOST_PP_DIV(x,y) x / y
BOOST_PP_INC(x) x + 1
BOOST_PP_MOD(x,y) x % y
BOOST_PP_MUL(x,y) x * y
BOOST_PP_SUB(x,y) x – y

整型逻辑运算

1
2
3
4
5
6
BOOST_PP_AND(x,y)          x && y
BOOST_PP_NOR(x,y) !(x || y)
BOOST_PP_OR(x,y) x || y
BOOST_PP_XOR(x,y) (bool)x != (bool)y ? 1 : 0
BOOST_PP_NOT(x) x ? 0 : 1
BOOST_PP_BOOL(x) x ? 1 : 0

位逻辑运算(单个位操作的)

1
2
3
4
5
BOOST_PP_BITAND(x,y) x && y
BOOST_PP_BITNOR(x,y) !(x || y)
BOOST_PP_BITOR(x,y) x || y
BOOST_PP_BITXOR(x,y) (bool)x != (bool)y ? 1 : 0
BOOST_PP_COMPL(x) x ? 0 : 1

比较运算

1
2
3
4
5
6
BOOST_PP_EQUAL(x,y)                               x == y ? 1 : 0
BOOST_PP_NOT_EQUAL(x,y) x != y ? 1 : 0
BOOST_PP_LESS(x,y) x < y ? 1 : 0
BOOST_PP_LESS_EQUAL(x,y) x <= y ? 1 : 0
BOOST_PP_GREATER(x,y) x > y ? 1 : 0
BOOST_PP_GREATER_EQUAL(x,y) x >= y ? 1 : 0

BOOST_PP序列

序列 (简称为 seq)是一组相邻的带括号的元素。例如, (a)(b)©(d) seq 不能为空。因此,一个 “空的” seq 被认为是一种特殊情况,必须由C++单独处理。 对于序列的处理效率是非常高的,可以认为是随机访问的。

BOOST_PP_SEQ_HEAD(seq) 展开为一个 seq 的第一个元素。 BOOST_PP_SEQ_TAIL(seq) 展开为一个 seq 中除了第一个元素以外的其它元素。(还是一个序列)

例如:

1
2
3
4
5
6
7
#include <boost/preprocessor/seq/seq.hpp>

#define SEQ (a)(b)(c)

BOOST_PP_SEQ_HEAD(SEQ) // expands to a
BOOST_PP_SEQ_TAIL(SEQ) // expands to (b)(c)
BOOST_PP_SEQ_CAT(SEQ) // expands to abc

BOOST_PP_IF(cond, t, f)

类似三元运算符 cond ? t : f ;