2013年12月24日星期二

boost bind及function的简单实现 - twoon

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
boost bind及function的简单实现 - twoon  阅读原文»

前面在做http server的时候,需要做一个回调的接口,要求能够绑定类的函数以及普通的函数到这个回调里,对于这种应用要求,选择boost的bind和function是最合适不过了,但现在事情有些不同,我不准备在现在做的这个东西里加入boost, 本着以造轮子为乐的精神,现在只能捋起袖子自己来搞一个。

使用的时候一直没有太留意它们的实现,现在要做起来,发现也不是想像中那么轻而易举。这个东西做到最后要实现的效果就是设计一个泛型的function holder,这个holder既能包装类的函数,又要能包装一般的函数,换言之就是能像下面一样来使用。

#include <iostream>

using namespace std;

class cs
{
public:

int proc(double d) { cout << "mem func proc:" << d << endl; return (int)d;}
};


int Proc(double d)
{
cout
<< "normal proc:" << d << endl;
return (int)d;
}


int main()
{
function fun
= &Proc;
fun(
2.3);

cs c;
fun
= bind(&cs::proc, &c);
fun(
3.3);

return 0;
}


一开始你可能会想,piece of cake! 直接封装一个function就行了。

template<class ret_type, class arg_type>
class function1: public copyable
{
public:
typedef ret_type (
* NORM_PROC) (arg_type);

function1(NORM_PROC proc
= 0): fun_(proc){}

ret_type
operator() (arg_type arg) { fun_->operator()(arg); }

private:

NORM_PROC fun_;
};

好,这个类可以封装一般的函数了,那类的函数呢?one more!

template<class CS, class ret_type, class arg_type>
class function2: public copyable
{
public:

function2(CS obj): obj_(obj) {}

ret_type
operator() (arg_type arg) { return obj_(arg); }

private:
CS obj_;
};

很快我们就发现有问题了,function1和function2是两不同的模板类,bind()的时候没法处理:bind()返回的应该要是一个统一的类型。

怎么办呢?我们可能想到要抽取出一个基类来,细路是对的!但还有些细节要处理。

比如:bind()返回的是什么类型呢?function1,function2的基类吗?这好像做不到,不能直接返回object。

template<class ret_type, class arg_type>
class function_base:public copyable
{
public:
ret_type
operator() (arg_type arg) = 0;
};

template
<class CS, class ret_type, class arg_type>
class function2: public function_base
{
public:

function2(CS obj): obj_(obj) {}

ret_type
operator() (arg_type arg) { return obj_(arg); }

private:
CS obj_;
};

template
<class CS, class ret_type, class arg_type>
function_base
<ret_type, arg_type> bind(ret_type (CS::* proc)(arg_type), CS* pc)
{
function2
<CS, ret_type, arg_type> func_holder(pc, proc);
return func_holder; //object slicing
}

那直接返回指针不就完了!

返回指针可行,但不好用,而且容易内存泄漏。解决的办法是对返回的指针再包一层,嗯,RAII.

等等,好像如果再包一层,就已经能直接隔开底下的function holder与具体的调用了啊!Perfect!

template<class ret_type, class arg_type>
class function_base:public copyable
{
public:
ret_type
operator() (arg_type arg) = 0;
};

template
<class ret_type, class arg_type>
class function1:public function_base
{
public:

typedef ret_type (
* NORM_PROC) (arg_type);


function1(NORM_PROC proc
= 0): fun_(proc){}


ret_type
operator() (arg_type arg) { fun_->operator()(arg); }


private:


NORM_PROC fun_;

};

template
<class CS, class ret_type, class arg_type>
class function2: public function_base
{
public:

function2(CS obj): obj_(obj) {}

ret_type
operator() (arg_type arg) { return obj_(arg); }

private:
CS obj_;
};

template<class ret_type, class arg_type>
class functioin:public copyable
{
public:

function(function_base<ret_type, arg_type>* pf): _obj(pf) {}
ret_type ubuntu 启动流程 - 壮壮熊  阅读原文»

ubuntu的init方式有两种:一种是System V initialization,一种是Upstart。ubuntu6.10以前的版本是第一种方式,之后的版本是第二种方式。

  在旧式的System V initialization中,/etc/inittab文件可是相当的重要。init进程启动后第一时间就是找这个文件。它负责系统的初始化,设置系统的runlevel及进入各个runlevel对应要执行的命令。决定启动级别的语句是id:5:initdefault这一行(这里设置的runlevel是5)。假设当前的inittab设置的runlevel就是5,则init会运行/etc/init.d/rc 5(最后面的5是传入的参数)命令,该命令会依据系统服务的依赖关系遍历执行/etc/rc5.d中的脚本/程序,来启动系统。我们可以打开/etc/rc5.d文件,会发现里面都是到/etc/init.d下文件的软链接。

1 zqx@zqx-K40AB:/etc$ ls -l rc5.d/
2 总用量 4
3 -rw-r--r-- 1 root root 677 4月 14 2012 README
4 lrwxrwxrwx 1 root root 20 10月 27 18:20 S20kerneloops -> ../init.d/kerneloops
5 lrwxrwxrwx 1 root root 27 10月 27 18:20 S20speech-dispatcher -> ../init.d/speech-dispatcher
6 lrwxrwxrwx 1 root root 20 10月 29 22:11 S31atieventsd -> ../init.d/atieventsd
7 lrwxrwxrwx 1 root root 20 10月 27 18:20 S50pulseaudio -> ../init.d/pulseaudio
8 lrwxrwxrwx 1 root root 15 10月 27 18:20 S50rsync -> ../init.d/rsync
9 lrwxrwxrwx 1 root root 15 10月 27 18:20 S50saned -> ../init.d/saned
10 lrwxrwxrwx 1 root root 19 10月 27 18:20 S70dns-clean -> ../init.d/dns-clean
11 lrwxrwxrwx 1 root root 18 10月 27 18:20 S70pppd-dns -> ../init.d/pppd-dns
12 lrwxrwxrwx 1 root root 14 10月 27 18:20 S75sudo -> ../init.d/sudo
13 lrwxrwxrwx 1 root root 22 10月 27 18:20 S99acpi-support -> ../init.d/acpi-support
14 lrwxrwxrwx 1 root root 21 10月 27 18:20 S99grub-common -> ../init.d/grub-common
15 lrwxrwxrwx 1 root root 18 10月 27 18:20 S99ondemand -> ../init.d/ondemand
16 lrwxrwxrwx 1 root root 18 10月 27 18:20 S99rc.local -> ../init.d/rc.local

  我们再来看看这几年才提出来的Upstart方式,这种是基于事件模式的,这种方式可以在系统运转的任何时期都可以通过发送事件来启动或终止服务。当然了,System V initialization方式也可以通过etc/init.d/Servicetart/stop/otherCommand来操作服务,但很明显不如Upstart方式简洁明白。

  Ubuntu现行的系统是兼容System V initialization方式的,我目前PC上的系统是Ubuntu 12.4的,可以看到系统中有如下几个目录:

1 /etc/init
2 /etc/init.d
3 /etc/rc${runlevel}.d

  作为两种init方式各自特征的/etc/init.d,/etc/rc${runlevel}.d目录和/etc/init目录在Ubuntu中都有了,那么Ubuntu是如何实现兼容的?实际上,Ubuntu中并没有直接采用System V-style启动服务,要知道,Ubuntu中的init已被替换为Upstart init,而System V-style的服务是存放于/etc/rc${runlevel}.d目录中的,(而/etc/rc${runlevle}.d/下的文件是到/etc/init.d的软链接)可Upstart init并不会直接跑到这里面去启动服务。它是通过间接调用来启动这类服务的。换句话说,Ubuntu中的init并不会直接奔着/etc/init.d或者/etc/rc${runlevel}.d/而去,它采用了折衷的办法,通过/etc/init下的某些配置文件调用/etc/rc${runlevel}.d/中的脚本以启动采用旧式System V-style的服务

  看以下的例子:

  进入/etc/init目录(Upstart init会到该目录下读取配置文件),有几个rc文件

1 rc.conf
2 rc-sysinit.conf
3 rcS.conf

  rc-sysinit在startup事件发生时被启动,rc在系统runlevel变化时被启动,rcS在系统runlevel为S时启动。在配置文件的注释中说明了,这几个文件,正是Upstart init处理System V-style服务的关键。

  rc-sysinit在startup事件发生时被启动,即,Upstart init会首先读取rc-sysinit.conf并执行相关配置和脚本。rc-sysinit.conf的主要工作是设置系统默认runlevel,检测是否存在/etc/inittab或内核命令行,若存在,则按内核命令行>/etc/inittab>默认runlevel的顺序设置系统 runlevel。最后,调用telinit进入设置的runlevel。

  由于调用了telinit进入了设定的runlevel,runlevel改变的事件发生,此时rc服务启动(当然其他服务也会)。那么,我们就有必要来看看rc.conf中到底有什么东西。打开rc.conf,注意到最后一行:

exec /etc/init.d/rc $RUNLEVEL

  是不是感觉/etc/init.d/rc很熟悉,没错,从这里开始,就是System V initialization方式的东西了。在System V initialization中,/etc/inittab中的各runlevel对应的命令行就是通过这种形式设置的。

  很明显,/etc/init.d/rc被调用了,并且传入了早前设置好的系统runlevel作为参数。而/etc/init.d/rc会根据传入 的runlevel参数调用/etc/rc${runlevel}.d/下的脚本(以S开头)以启动服务,终止在前次runlevel启动而当前在 runlevel需要终止的服务。通过rc-sysinit和rc间接的调用/etc/init.d/rc从而启动System V-style服务,Ubuntu在采用新式Upstart init照顾了旧式的System V init。


本文链接:http://www.cnblogs.com/zhouqinxiong/p/3490391.html,转载请注明。

阅读更多内容

没有评论:

发表评论