大名鼎鼎的Numerical Recipes出的C++版程序不知方便了多少苦逼的研究僧,但是好歹不是专业程序员开发的,很多地方还存在语法规范上的问题。
类私有变量的初始化顺序有问题也就算了,我可以给改过来,但是函数或类的定义和申明没有分开就是个大问题了(所有代码都在头文件中),使得我的代码中老是出现重定义错误。初略地分析了一下发现,对于struct,申明和定义放在一起是没有问题的,但是函数就头疼了。
遇到这样的问题,如果把函数定义和申明分开,那么这个代码库就不能单独存在,于是乎最后我的各种源代码里散落着NR的代码,碰到NR语法上的一些瑕疵改起来就很不方便。今晚突然灵机一动,既然struct不会出现重定义错误,那么何不把函数也包装成struct呢?
C++真是个好东西,括弧()
操作符重载使得对一个对象可以像函数一样使用。下面是我的基本解决思路:
// func.h中有一个函数叫A
bool A(int a){
return a<2;
}
// 换成下面这个样子
struct A_func{
bool operator()(int a){
return a<2;
}
}
#define A A_func() //这是一种解决方式,但是这样做有个弊端,那就是不能再有叫A的对象。
static A_func A; //用静态外部变量来做更好,不会出现A重定义的问题
通过这样一个包装的过程,在不同的源文件中include “func.h”均不会引起最后链接时的重定义问题,因为静态外部变量仅在当前文件内有效。更爽的是,实测发现采用函数对象竟然比直接使用函数效率更高,这就是内联的成果?
PS: 好吧,我SB了,函数也可以定义成静态的(在前面加上static),从而将其限制在当前文件,就不会出现重定义错误了。
再次PS: 好吧,原来还有更好的方法,就是把多个函数放到未命名名字空间中去即可避免函数重定义,即形如
namespace{
void func1(){}
void func2(){}
.........
}
下面是采用函数对象时内联优化的效果测试:
/* g++ -Wall -fopenmp -std=c++0x main.cpp -lrt */
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
#define Doub double
#define Int int
struct gammln_func{
Doub operator()(const Doub xx) {
Int j;
Doub x,tmp,y,ser;
static const Doub cof[14]={57.1562356658629235,-59.5979603554754912,
14.1360979747417471,-0.491913816097620199,.339946499848118887e-4,
.465236289270485756e-4,-.983744753048795646e-4,.158088703224912494e-3,
-.210264441724104883e-3,.217439618115212643e-3,-.164318106536763890e-3,
.844182239838527433e-4,-.261908384015814087e-4,.368991826595316234e-5};
if (xx <= 0) throw("bad arg in gammln");
y=x=xx;
tmp = x+5.24218750000000000;
tmp = (x+0.5)*log(tmp)-tmp;
ser = 0.999999999999997092;
for (j=0;j<14;j++) ser += cof[j]/++y;
return tmp+log(2.5066282746310005*ser/x);
}
};
static struct gammln_func gammlno;
Doub gammlnf(const Doub xx) {
Int j;
Doub x,tmp,y,ser;
static const Doub cof[14]={57.1562356658629235,-59.5979603554754912,
14.1360979747417471,-0.491913816097620199,.339946499848118887e-4,
.465236289270485756e-4,-.983744753048795646e-4,.158088703224912494e-3,
-.210264441724104883e-3,.217439618115212643e-3,-.164318106536763890e-3,
.844182239838527433e-4,-.261908384015814087e-4,.368991826595316234e-5};
if (xx <= 0) throw("bad arg in gammln");
y=x=xx;
tmp = x+5.24218750000000000;
tmp = (x+0.5)*log(tmp)-tmp;
ser = 0.999999999999997092;
for (j=0;j<14;j++) ser += cof[j]/++y;
return tmp+log(2.5066282746310005*ser/x);
}
const int N=100000;
int main(int argc, const char *argv[])
{
{
struct timespec tpstart,tpend;
clock_gettime(CLOCK_MONOTONIC, &tpstart);
Doub tmp = 0;
for(int i=0; i<N; i++) {
tmp=gammlnf(10.0);
}
clock_gettime(CLOCK_MONOTONIC, &tpend);
long timedif = (tpend.tv_sec-tpstart.tv_sec)*1000*1000+(tpend.tv_nsec-tpstart.tv_nsec)/1000;
cout<<"func:\t"<<tmp<<"\ttime:"<<timedif<<endl;
}
{
struct timespec tpstart,tpend;
clock_gettime(CLOCK_MONOTONIC, &tpstart);
Doub tmp = 0;
for(int i=0; i<N; i++) {
tmp=gammlno(10.0);
}
clock_gettime(CLOCK_MONOTONIC, &tpend);
long timedif = (tpend.tv_sec-tpstart.tv_sec)*1000*1000+(tpend.tv_nsec-tpstart.tv_nsec)/1000;
cout<<"obj:\t"<<tmp<<"\ttime:"<<timedif<<endl;
}
return 0;
}