Android with root Git for version control Lircd with Raspberry Pi for IR receiver and sender Tips for Windows Depolying your own password management tool -- KeeWeb Depoly your flask app into Heroku Fix shit IE code manually ISBN to Book Category by Scraping DangDang A Generic Makefile for C/C++ Program Configure Raspberry pi Remove watermark with PyPDF2 tips for docker Anaconda+TensorFlow+CUDA Snippets Configure Remote Mathematica Kernel Build your own ngrok server Access Array SSL VPN 使用Rstudio制作html5幻灯片 tips for Mac OS X system Tips for ipython notebook 配置Ubuntu server + Openbox (Obuntu) tips for Vimperator tips for Vim 安装CUDA My First Jekyll Blog rsync常见选项 在Linux中读取Ipod touch的文件 tip for texmacs 在VPS上建站的一些tip Gnuplot绘图札记 Samba系统和autofs自动挂载 Linux中alsamixer声卡无法录音 搭建自己的RSS订阅器——Tiny Tiny RSS Grub2引导安装Ubuntu awk tips 将Ubuntu系统装入U盘 The Great Rtorrent 编译GCC 再这样剁手!!!该死的libgd 使用ulimit进行资源限制 使用SSH代理上IPV6 使用RCurl抓取网页数据 修复Ubuntu Grub记 openbox中的文件关联 在Ubuntu 12.04下编译qtiplot 处理BCM4312网卡驱动纪实 配置我的Ubuntu Server记 Cygwin杂记 Linux 使普通用户具有以超级权限执行脚本 让firefox自定义地处理文件类型 WordPress优秀主题及插件 在phpcloud上搭建wordpress UBUNTU下用pptpd做VPN server ubuntu升级内核过后的一些问题 安装telnet服务 kubuntu札记 64位kubuntu札记 统计软件R Virtualbox stardict星际译王 Ubuntu重装windows系统后的grub引导修复 SSH服务及花生壳域名解析 采用cbp2make工具由code::blocks工程创建makefile文件 UBUNTU 札记

函数对象的妙用-避免函数重定义

2013年04月24日

大名鼎鼎的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;
}