CRTP模式概述
CRTP(奇异模板递归,Curiously Recurring Template Pattern)模式实现了接口继承,但没有虚函数表的开销。所有的类型检查、函数生成全都在编译期完成。
缺点: IDE几乎没法做到正确跳转。
基础架构
fwd.hpp —— 类型萃取与基类定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| namespace pinocchio { template<class C> struct traits { }; template<class Derived> struct NumericalBase { typedef typename traits<Derived>::Scalar Scalar; }; }
|
se3.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
|
#define PINOCCHIO_SE3_TYPEDEF_GENERIC(Derived, TYPENAME) \ typedef TYPENAME traits<Derived>::Scalar Scalar; \ typedef TYPENAME traits<Derived>::AngularType AngularType; \ typedef TYPENAME traits<Derived>::LinearType LinearType; \ typedef TYPENAME traits<Derived>::AngularRef AngularRef; \ typedef TYPENAME traits<Derived>::LinearRef LinearRef; \ typedef TYPENAME traits<Derived>::ConstAngularRef ConstAngularRef; \ typedef TYPENAME traits<Derived>::ConstLinearRef ConstLinearRef; \ typedef TYPENAME traits<Derived>::ActionMatrixType ActionMatrixType; \ typedef TYPENAME traits<Derived>::HomogeneousMatrixType HomogeneousMatrixType; \ typedef TYPENAME traits<Derived>::PlainType PlainType; \ enum \ { \ Options = traits<Derived>::Options, \ LINEAR = traits<Derived>::LINEAR, \ ANGULAR = traits<Derived>::ANGULAR \ }
#define PINOCCHIO_SE3_TYPEDEF_TPL(Derived) PINOCCHIO_SE3_TYPEDEF_GENERIC(Derived, typename)
#define PINOCCHIO_SE3_TYPEDEF(Derived) \ PINOCCHIO_SE3_TYPEDEF_GENERIC(Derived, PINOCCHIO_MACRO_EMPTY_ARG) #include "pinocchio/spatial/se3-base.hpp" #include "pinocchio/spatial/se3-tpl.hpp"
|
se3-base.hpp —— CRTP父类接口定义
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
| namespace pinocchio { template<class Derived> struct SE3Base : NumericalBase<Derived> { PINOCCHIO_SE3_TYPEDEF_TPL(Derived); Derived & derived() { return *static_cast<Derived *>(this); } const Derived & derived() const { return *static_cast<const Derived *>(this); } ConstAngularRef rotation() const { return derived().rotation_impl(); } } }
|
se3-tpl.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 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 62 63 64 65 66 67
| namespace pinocchio { template<typename _Scalar, int _Options> struct traits<SE3Tpl<_Scalar, _Options>> { enum { Options = _Options, LINEAR = 0, ANGULAR = 3 }; typedef _Scalar Scalar; typedef Eigen::Matrix<Scalar, 3, 1, Options> Vector3; typedef Eigen::Matrix<Scalar, 4, 1, Options> Vector4; typedef Eigen::Matrix<Scalar, 6, 1, Options> Vector6; typedef Eigen::Matrix<Scalar, 3, 3, Options> Matrix3; typedef Eigen::Matrix<Scalar, 4, 4, Options> Matrix4; typedef Eigen::Matrix<Scalar, 6, 6, Options> Matrix6; typedef Matrix3 AngularType; typedef typename PINOCCHIO_EIGEN_REF_TYPE(Matrix3) AngularRef; typedef typename PINOCCHIO_EIGEN_REF_CONST_TYPE(Matrix3) ConstAngularRef; typedef Vector3 LinearType; typedef typename PINOCCHIO_EIGEN_REF_TYPE(Vector3) LinearRef; typedef typename PINOCCHIO_EIGEN_REF_CONST_TYPE(Vector3) ConstLinearRef; typedef Matrix6 ActionMatrixType; typedef Matrix4 HomogeneousMatrixType; typedef SE3Tpl<Scalar, Options> PlainType; };
template<typename _Scalar, int _Options> struct SE3Tpl : public SE3Base<SE3Tpl<_Scalar, _Options>> { EIGEN_MAKE_ALIGNED_OPERATOR_NEW
PINOCCHIO_SE3_TYPEDEF_TPL(SE3Tpl); typedef SE3Base<SE3Tpl<_Scalar, _Options>> Base; typedef Eigen::Quaternion<Scalar, Options> Quaternion; typedef typename traits<SE3Tpl>::Vector3 Vector3; typedef typename traits<SE3Tpl>::Matrix3 Matrix3; typedef typename traits<SE3Tpl>::Matrix4 Matrix4; typedef typename traits<SE3Tpl>::Vector4 Vector4; typedef typename traits<SE3Tpl>::Matrix6 Matrix6;
using Base::rotation;
ConstAngularRef rotation_impl() const { return rot; } AngularRef rotation_impl() { return rot; } void rotation_impl(const AngularType & R) { rot = R; } }
|
CRTP模式核心要点
模板延迟实例化(惰性编译) —— 父类声明时引用 traits<Derived>::Scalar,此时类型尚未定义,但不会报错。只有当模板被实际实例化时,编译器才会检查类型是否存在。
编译期类型检查 —— 子类如果没有正确特化 traits,编译期直接报错,起到强制接口约束的作用。
零运行时开销 —— 没有虚函数表,没有虚函数调用开销,所有调用在编译期确定,性能等同于普通函数调用。
套娃式继承 —— 子类 SE3Tpl 继承自父类 SE3Base<SE3Tpl>,父类模板参数就是子类自己。
derived() 向下造型 —— 父类通过 static_cast 将 this 指针转换为子类指针,从而调用子类的 *_impl() 实现函数。