目录
1 概述
2 常见的预处理指令
3 其他预编译指令
4 预定义标识符
5 #define
5.1 使用宏函数,定义类的成员函数
5.2 Eigen中使用宏函数,重写类的成员函数(内存分配方式)
1 概述
预处理指令提供按条件跳过源文件中的节、报告错误和警告条件,以及描绘源代码的不同区域的能力。比如 使用术语“预处理指令”为了与 C 和?C++?编程语言保持一致。
预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有:文件包含,条件编译、布局控制和宏替换4种。 文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。 条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。 布局控制:#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。 宏替换:#define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
2 常见的预处理指令
● #define 宏定义 ● #undef 未定义宏 ● #include 文本包含 ● #ifdef 如果宏被定义就进行编译 ● #ifndef 如果宏未被定义就进行编译 ● #endif 结束编译块的控制 ● #if 表达式非零就对代码进行编译 ● #else 作为其他预处理的剩余选项进行编译 ● #elif 这是一种#else和#if的组合选项 ● #line 改变当前的行数和文件名称 ● #error 输出一个错误信息 ● #pragma 为编译程序提供非常规的控制流信息
3 其他预编译指令
除了上面我们说的集中常用的编译指令,还有3种不太常见的编译指令:#line、#error、#pragma,我们接下来就简单的谈一下。 #line的语法如下:
例如:#line 30 a.h 其中,文件名a.h可以省略不写。 这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号为30,文件名是a.h。初看起来似乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,我们知道编译器对C++源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行分析。
例如:
这条指令主要是给出错误信息,上面的这个例子就是,如果没有在UNIX环境下,就会输出This software requires the UNIX OS.然后诱发编译器终止。所以总的来说,这条指令的目的就是在程序崩溃之前能够给出一定的信息。
4 预定义标识符
为了处理一些有用的信息,预处理定义了一些预处理标识符,虽然各种编译器的预处理标识符不尽相同,但是他们都会处理下面的4种: 1、__FILE__ 正在编译的文件的名字 2、__LINE__ 正在编译的文件的行号 3、__DATE__ 编译时刻的日期字符串,例如: "25 Dec 2000" 4、__TIME__ 编译时刻的时间字符串,例如: "12:30:55" 例如:cout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<<endl;
5 #define
#define M 5 //无参宏, 代码中所有使用M的地方在编译时会被替换成5
#define COUNT(M) M * M //有参宏, M最好加上括号
//使用:printf("COUNT = %d\n", COUNT(10)); // 替换为: COUNT(10) = 10 * 10
// 输出结果: COUNT = 100
宏名中不能有空格,宏名与形参表之间也不能有空格,而形参表中形参之间可以出现空格:
#define SUM (a,b) a + b //定义有参宏
// printf("SUM = %d\n", SUM(1,2)); //调用有参宏。Build Failed!
// 因为 SUM 被替换为:(a,b) a + b
//正确写法:
#define SUM(a,b) (a) + (b)
5.1 使用宏函数,定义类的成员函数
#define EIGEN_MAX_ALIGN_BYTES 1
#if EIGEN_MAX_ALIGN_BYTES!=0 //当条件满足就定义两个函数
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign) \
void functionA1(int num) { \
std::cout << "==> call functionA1:" << NeedsToAlign << std::endl; \
} \
void functionA2(int num) { \
std::cout << "==> call functionA2:" << num << std::endl; \
}
#else //当条件不满足,就定义一个空(什么也不定义)
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
#endif
class EigenTest{
public:
enum { NeedsToAlign = 230 };
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign);
};
int main(int argc, char* argv[])
{
EigenTest et;
et.functionA1(100); // 当 EIGEN_MAX_ALIGN_BYTES等于0时,et就没有functionA1、functionA2两个成员函数的定义
return 0;
}
5.2 Eigen中使用宏函数,重写(override)类的成员函数(内存分配函数)
先来些宏定义,对分配内存new delete的函数进行重新定义。这里的NeedsToAlign作为宏函数的参数,传入到函数体中作为一个模板参数(或者普通形参)使用。若EIGEN_MAX_ALIGN_BYTES=0, 就定义一个空的宏(啥也不做,宏替换的地方什么也没有)
/*****************************************************************************
*** Implementation of EIGEN_MAKE_ALIGNED_OPERATOR_NEW [_IF] ***
*****************************************************************************/
#if EIGEN_MAX_ALIGN_BYTES!=0
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_NOTHROW(NeedsToAlign) \
void* operator new(std::size_t size, const std::nothrow_t&) EIGEN_NO_THROW { \
EIGEN_TRY { return Eigen::internal::conditional_aligned_malloc<NeedsToAlign>(size); } \
EIGEN_CATCH (...) { return 0; } \
}
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign) \
void *operator new(std::size_t size) { \
return Eigen::internal::conditional_aligned_malloc<NeedsToAlign>(size); \
} \
void *operator new[](std::size_t size) { \
return Eigen::internal::conditional_aligned_malloc<NeedsToAlign>(size); \
} \
void operator delete(void * ptr) EIGEN_NO_THROW { Eigen::internal::conditional_aligned_free<NeedsToAlign>(ptr); } \
void operator delete[](void * ptr) EIGEN_NO_THROW { Eigen::internal::conditional_aligned_free<NeedsToAlign>(ptr); } \
void operator delete(void * ptr, std::size_t /* sz */) EIGEN_NO_THROW { Eigen::internal::conditional_aligned_free<NeedsToAlign>(ptr); } \
void operator delete[](void * ptr, std::size_t /* sz */) EIGEN_NO_THROW { Eigen::internal::conditional_aligned_free<NeedsToAlign>(ptr); } \
/* in-place new and delete. since (at least afaik) there is no actual */ \
/* memory allocated we can safely let the default implementation handle */ \
/* this particular case. */ \
static void *operator new(std::size_t size, void *ptr) { return ::operator new(size,ptr); } \
static void *operator new[](std::size_t size, void* ptr) { return ::operator new[](size,ptr); } \
void operator delete(void * memory, void *ptr) EIGEN_NO_THROW { return ::operator delete(memory,ptr); } \
void operator delete[](void * memory, void *ptr) EIGEN_NO_THROW { return ::operator delete[](memory,ptr); } \
/* nothrow-new (returns zero instead of std::bad_alloc) */ \
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_NOTHROW(NeedsToAlign) \
void operator delete(void *ptr, const std::nothrow_t&) EIGEN_NO_THROW { \
Eigen::internal::conditional_aligned_free<NeedsToAlign>(ptr); \
} \
typedef void eigen_aligned_operator_new_marker_type;
#else
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
#endif
// 注意接下来这句话:在使用中经常用到它
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(true)
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar,Size) \
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(bool(((Size)!=Eigen::Dynamic) && ((sizeof(Scalar)*(Size))%EIGEN_MAX_ALIGN_BYTES==0)))
在需要重载的类的new delete等上述分配内存的方法中,使用该宏函数,比如
template<typename Derived>
class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
#endif
{
public:
//...
public:
enum { NeedsToAlign = (SizeAtCompileTime != Dynamic) && (internal::traits<Derived>::Alignment>0) };
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
//...
};
当我们使用Eigen时,在类中需要重载内存分配的new delete函数时,加上这句话即可:EIGEN_MAKE_ALIGNED_OPERATOR_NEW ,比如:使用g2o优化时,定义一个顶点,需要使用eigen中的一些底层函数,所以如下:
// 曲线模型的顶点,模板参数:优化变量维度和数据类型
class CurveVertex: public g2o::BaseVertex<4, Eigen::Vector4d> {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW //使用时,加上此句,等于采用eigen中约定的方式重载了该类的new delete等内存分配函数
virtual void setToOriginImpl(){
_estimate << 0, 0, 0;
}
virtual void oplusImpl(const double* update) {
_estimate += Eigen::Vector4d(update);
}
virtual bool read(istream & in) {}
virtual bool write(ostream & out) const {}
};
|