涛's profile徒然草的新园子PhotosBlogListsMore Tools Help

徒然草的新园子

陌上花开,可缓缓归矣
4/29/2009

ZFS

ZFS是一个128的文件系统,这意味着它能存储1800亿亿(18.4 × 1018)倍于当前64文件系统的数据。ZFS的设计如此超前以至于这个极限就当前现实际可能永远无法遇到。项目领导Bonwick曾说:“要填满一个128位的文件系统,将耗尽地球上所有存储设备。除非你拥有煮沸整个海洋的能量,不然你不可能将其填满。(Populating 128-bit file systems would exceed the quantum limits of earth-based storage. You couldn't fill a 128-bit storage pool without boiling the oceans.

 

-----------------------

Best regards,

Xie, Tao

 

4/9/2009

winxp+vmware+ubuntu

想玩hadoop然后直接在开发机(winxp)上部署发现忒复杂……
然后在开发机上装了一个vmware,vmware上装ubuntu,网络我本来想配置成bridge模式,能让ubuntu直接访问公司局域网内的其他机器,但总是不成功
折腾N久还是用了NAT模式,等有时间好好研究研究为啥,我对network可是一窍不通,怀疑大学时我干嘛去了……
3/13/2009

《我的团长我的团》

“我是说,做学生的时候想着当兵,抗击日寇,脑子里的景是所有人往上冲,我是其中的一个。当了兵,我真冲了,迎面炮弹炸出的热气,屁股后莫名其妙地生凉气,我回头一看,我一个,其他人在战壕里乐。”我说。
  很多人在笑,看起来有很多人熟悉这么个场景,但我没笑,虞啸卿也没笑。
  “我再也不冲了,我想傻瓜才第一个冲,我也不第二个冲,第二个是白痴。可总得有人冲。我做连副,最拿手就是给新兵煽风点火,让他们冲头,老兵跟在后边捡便宜或者捡命。老兵命金贵,打过几仗还没死的人尤其金贵,而且他跟你认识了,熟了,成哥们儿了。新兵通常冲一次就玩完,你不要认识他,那是炮灰。我手上光煽乎上去报销的炮灰就一百多。久了,觉得对不住。我想要有个人带我们一起冲好了,没猜忌,大家一起,可没这人,我们还是吵着骂着,谁都不服,谁都不信,勇敢,但是虚弱。可没这人。现在我们有一个了,他几乎把我们活着带到东岸……”

这是孟烦啦说的话,看电视进度太慢,就下载了兰晓龙的这部小说看,看到虞啸卿审龙文章这章,觉得写得精彩。龙文章的很多故事都在被审的过程中一一讲述。让我们看到了一个经历过N多败仗,看着N多人死去,不想坐等死的军人。喜欢这个电影是因为《士兵突击》,可是《士兵》里讲的战争不真实,有军队里人说,他们都不怎么看的,不真实。

我一直觉得真实的打仗,大家都怕死,命都比金贵,谁都是爹妈生的。也只有学生兵,稚气未脱会听着号子就去做炮灰,像上面这段写的真的很贴切。老兵油子命最贵,但是连他们都到了看多了死尸良心不安不想再坐等死的地步,想有个人带着往前冲的地步。嘿嘿。就像兽医说烦了跟其他人比烂,其实一群比着烂的人心里都不想烂下去的。有个疯子点了把火,就开始燎原了。

3/12/2009

Ruby on Rails

这两天还在闲,随便看了一些东西,首先在网上看了一些Cloud Computing的东西,然后去看MapReduce这个概念,有兴趣的可以去看Google的论文《MapReduce: Simplified Data Processing on Large Clusters》,参考了google出来的几个博客上的介绍,了解了一点点。
http://www.cnblogs.com/huanghao1986/archive/2008/12/07/1349682.html
http://www.mengyan.org/blog/archives/2006/11/15/138.html 
然后在看某人的博客的时候,里面一篇提到用Rails去做Web开发,觉得自己对于Web开发所知实在有限,还停留在J2EE那个概念上,后来的什么AJAXStrutsSpring俺都不知道了。心向往之,就去看了一下,发觉用Ruby on Rails真的可以称之为Agile,别人介绍去看一本好书《Agile Web Development with Rails》,一口气看了一百多页(这本书有七百多页……),这本书好玩的就是你可以跟着做一个小的project,卖书网站,从中可以直觉感受到是RoR是多么的迅捷。这本书的第三部分开始讲Rails的框架了,就是让你知道了RoR有多强后开始剖析RoR了,看到这我有点累了……电子书看得时间久了对眼睛真不好,不过电子书做的很漂亮,有人要的话我给你一份,嘿嘿。 有谁是做Web开发的,讲讲你们平时怎么搞的,让俺有个概念。忘说了,俺以前是做library的,不做这些,嗯。 
Btw,又下雨了,不爽,打不了球了,大家都说公司缺一个室内的篮球场……还是晚上回家看《我的团长》吧……

Best regards,

Xie,  Tao 

 

3/11/2009

谁是谁

 U2190P28T3D2398587F522DT20090302195630

3/6/2009

boost库中的traits学习笔记

boost中的traits分类:
1. Primary Type Categorisation(初级类型分类)
2. Secondary Type Categorisation(次级类型分类)
3. Type Properties(类型属性)
4. Relationships Between Types(类型间关系)
5. Transformations Between Types(类型间转换)
6. Synthesizing Types(类型合成)
7. Function Traits(函数traits)

本人尝试直接阅读相应源码,但因为太多宏、模板等目前来说还过于晦涩的语法、技巧的堆积,只好抄袭别人的笔记了。

  • 初级类型分类
    is_array (boost/type_traits/is_array.hpp)
    // 缺省
    template<typename T>
    struct is_array{
    static const bool value=false;
    };
    // 偏特化
    template<typename T,size_t N>
    struct is_array<T[N]>{
    static const bool value=true;
    };


    C++标准允许整型常量表达式作为模板参数,上面的N就是这样。这也说明出现在模板偏特化版本中的模板参数(在本例中为typename T,size_t N两个)个数不一定要跟缺省的(本例中为typename T一个)相同,但出现在类名称后面的参数个数却要跟缺省的个数相同(is_array<T[N]>,T[N]为一个参数,与缺省的个数相同)。

    使用
    is_array<int [10]>::value // true(T=int,N=10)
    is_array<int>::value // false(T=int)

    is_class(.../is_class.hpp)
    定义
    template <typename T>
    struct is_class_impl
    {
           template <class U>
           static ::yes_type is_class_tester(void(U::*)(void));

           template <class U> static ::no_type is_class_tester(...);

           // ice_and是一个元函数,提供逻辑与(AND)操作
           static const bool value = ::ice_and<
                 sizeof(is_class_tester<T>(0))==sizeof(::yes_type), // #3
                 ::ice_not<::is_union<T>::value >::value
                 >::value
    };
    template<typename T>
    struct is_class{
           // 所有实现都在is_class_imp中
           static const bool value = is_class_impl<T>::value;
    };
    注解
    ::boost::type_traits::yes_type是一个typedef:
    typedef char yes_type;
    因此sizeof(yes_type)为1.

    ::boost::type_traits::no_type则是一个struct:
    struct no_type{
           char padding[8];
    };
    因此sizeof(no_type)为8。
    这两个类型一般被用作重载函数的返回值类型,这样通过检查返回值类型的大小就知道到底调用了哪个函数,它们的定义位于“boost/type_traits/detail/yes_no_type.hpp”中。
    is_class_impl中有两个static函数,第一个函数仅当模板参数U是类时才能够被实例化,因为它的参数类型是void(U::*)(void),即指向成员函数的指针。第二个函数具有不定量任意参数列表,C++标准说只有当其它所有的重载版本都不能匹配时,具有任意参数列表(...)的重载版本才会被匹配。所以,如果T为类,则void (T::*)(void)这种类型就存在,所以对is_class_tester<T>(0)的重载决议将是调用第一个函数,因为将0赋给任意类型的指针都是合法的。而如果T不是类,则就不存在void(T::*)(void)这种指针类型,所以第一个函数就不能实例化,这样,对is_class_tester<T>(0)的重载决议结果只能调用第二个函数。
    现在注意#3处的表达式:
    sizeof(is_class_tester<T>(0))==sizeof(...::yes_type) // #3
    按照上面的推导,如果T为类,is_class_tester<T>(0)实际调用第一个重载版本,返回yes_type,则该表达式求值为true。如果T不是类,则is_class_tester<T>(0)调用第二个重载版本,返回no_type,则该表达式求值为false。这正是我们想要的。

    一个值得注意的地方是:在sizeof的世界里,没有表达式被真正求值,编译器只推导出表达式的结果的类型,然后给出该类型的大小
    比如,对于sizeof(is_class_tester<T>(0))编译器实际并不调用函数的代码来求值,而只关心函数的返回值类型。所以声明该函数就够了。另一个值得注意之处是is_class_tester的两个重载版本都用了模板函数的形式。第一个版本用模板形式的原因是如果不那样做,而是这样
    static yes_type is_class_tester(void(T::*)(void));
    的话,则当T不是类时,该traits将不能通过编译,原因很简单,当T不是类时void (T::*)(void)根本不存在。然而,使用模板时,当T不是类时该重载版本会因不能实例化而根本不编译,C++标准允许不被使用的模板不编译(实例化)。这样编译器就只能使用第二个版本,这正合我们的意思。
    而is_class_tester的第二个重载版本为模板则是因为第一个版本是模板,因为在#3处对is_class_tester的调用是这样的:
    is_class_tester<T>(0)
    如果第二版本不是模板的话,这样调用只能解析为对is_class_tester模板函数(即第一个版本)的调用,于是重载解析也就不复存在了。“等等!”你意识到了一些问题:“模板函数的调用可以不用显式指定模板参数!”好吧,也就是说你试图这样写:
    // 模板
    template <class U>
    static ...::yes_type is_class_tester(void(U::*)(void));
    // 非模板
    static ...::no_type is_class_tester(...);
    然后在#3标记的那一行这样调用:
    is_class_tester(0) // 原来是is_class_tester<T>(0))
    是的,我得承认,这的确构成了函数重载的条件,也的确令人欣喜的通过了编译,然而结果肯定不是你想要的。你会发现对所有类型T,is_class<T>::value现在都是0了!
    也就是说,编译器总是调用is_class_tester(..);这是因为,当调用的函数的所有重载版本中有一个或多个为模板时,编译器首先要尝试进行模板函数实例化而非重载决议,而在尝试实例化的过程中,编译器会进行模板参数推导,0的类型被编译器推导为int(0虽然可以赋给指针,但0的类型不可能被推导为指针类型,因为指针类型可能有无数种,而事实上C++是强类型语言,对象只能属于某一种类型),而第一个函数的参数类型void (U::*)(void)根本无法与int匹配(因为如果匹配了,那么模板参数U被推导为什么呢?)。所以第一个版本实例化失败后编译器只能采用非模板的第二个版本。结果如你所见,是令人懊恼的。然而如果你写的是is_class_tester<T>(0)你其实是显式实例化了is_class_tester每一个模板函数(除了那些不能以T为模板参数实例化的),而它们都被列入接受重载决议的侯选单,然后编译器要做的就只剩下重载决议了。(关于编译器在含有模板函数的重载版本时是如何进行重载决议的,可参见C++ Primer的Function Templates一节,里面有极其详细的介绍)。
    初级类型分类还有:
    is_void is_integral is_float is_pointer is_reference is_union is_enum is_function
  • 次级类型分类
    is_member_function_pointer(.../is_member_function_pointer.hpp)
    定义(.../detail/is_mem_fun_pointer_impl.hpp)

    // 缺省版本
    template <typename T>
    struct is_mem_fun_pointer_impl{
           static const bool value = false;
    };
    // 偏特化版本,匹配无参数的成员函数
    template <class R, class T >
    struct is_mem_fun_pointer_impl<R (T::*)() >{
           static const bool value = true;
    };
    //匹配一个参数的成员函数
    template <class R, class T , class T0>
    struct is_mem_fun_pointer_impl<R (T::*)( T0) >{
           static const bool value = true;
    };
    // 其它版本只是匹配不同参数个数的成员函数的偏特化而已,参见源文件。
    template<class T>
    struct is_mem_function_pointer{
           static const bool value = is_mem_fun_pointer_impl<T>::value;
    };

    注解
    假设你有一个类X,你这样判断:
    is_mem_function_pointer<int (X::*)(int)>::value
    则编译器会先将is_mem_function_pointer的模板参数class T推导为int (X::*)(int),然后将其传给is_mem_fun_pointer_impl,随后编译器寻找后者的偏特化版本中最佳匹配项为:is_mem_fun_pointer_impl<R(T::*)(T0)>
    其中R=int,T=X,T0=int。而该偏特化版本的::value=true。
    次级类型分类还有:
    is_arithmetic is_fundamental is_object is_scalar is_compound

  • 类型属性
    is_empty(.../is_empty.hpp)

    定义
    // 如果T是空类,那么派生类的大小就是派生部分的大小即sizeof(int)*256
    template <typename T>
    struct empty_helper_t1 : public T{
            empty_helper_t1();
            int i[256];
    };
    struct empty_helper_t2{
            int i[256];
    }; // 大小为sizeof(int)*256

    通过比较以上两个类的大小可以判断T是否为空类,如果它们大小相等则T为空类。反之则不为空。

    这里一个值得注意的地方是:若定义一个空类E,则sizeof(E)为1(这一个字节是用于在内存中唯一标识该类的不同对象。如果sizeof(E)为0,则意味着不同的对象在内存中的位置没有区别,这显然有违直观)。然而如果有另一个非空类继承自E,那么这一个字节的内存就不需要。也就是说派生类的大小等于派生部分的大小,而非加上一个字节。

    // 这个辅助类的作用是:如果T不是类则使用该缺省版本如果T是类则使用下面的偏特化版本。而判断T是否为类的工作则由上面讲过的is_class<>traits来做。

    template <typename T, bool is_a_class = false>
    struct empty_helper {
    static const bool value = false;
    };

    template <typename T>
    struct empty_helper<T, true> // #5
    {
    static const bool value = (sizeof(empty_helper_t1<T>) == sizeof(empty_helper_t2));
    };

    template <typename T>
    struct is_empty_impl{
    // remove_cv将T的const volatile属性去掉,这是因为在作为基类的类型不能有const/volatile修饰。
    typedef typename remove_cv<T>::type cvt;
    static const bool value = ice_or<
         empty_helper<cvt, is_class<T>::value>::value, // #4
         BOOST_IS_EMPTY(cvt)
         >::value;
    };

    注解
    在#4处,如果is_class<T>::value为true(即T为类)则empty_helper<cvt,is_class<T>::value>::value实际决议为empty_helper<cvt,true>,这将采用偏特化版本#5,则结论出现。否则T不是类,则采用缺省版本,结果::value为false。

    is_polymorphic(.../is_polymorphic.hpp)

    is_plymorphic的运作机制基于一个基本事实:一个多态的类里面会有一个虚函数表指针(一般称为vptr),它指向一个虚函数表(一般称为vtbl)。后者保存着一系列指向虚函数的函数指针以及运行时类型识别信息。一个虚函数表指针通常占用4个字节(32寻址环境下的所有指针都占用4个字节)。反之,如果该类不是多态,则没有这个指针的开销。基于这个原理,我们可以断定:如果类X不是多态类(没有vtbl及vptr),则如果从它派生一个类Y,Y中仅含有一个虚函数,这会导致sizeof(Y)>sizeof(X)(这是因为虚函数的首次出现导致编译器必须在Y中加入vptr的缘故)。反之,如果X原本就是多态类,则sizeof(Y)==sizeof(X)(因为这种情况下,Y中其实已经有了从X继承而来的vtbl及vptr,编译器所要做的只是将新增的虚函数纳入到vtbl中去)。

    定义
    // 当T为类时使用这个版本
    template <class T>
    struct is_polymorphic_imp1 {

    typedef typename remove_cv<T>::type ncvT;
    // ncvT是将T的const volatile修饰符去掉后的类型,因为public后不能跟这样的修饰符,该类里没有虚函数
    struct d1 : public ncvT {
    d1();
    ~d1() // throw();
    char padding[256];
    };

    struct d2 : public ncvT // 在d2中加入一个虚函数{
    d2();
    //加入一个虚函数,如果ncvT为非多态则会导致vptr的加入从而多占用4字节
    virtual ~d2() // throw();
    char padding[256];
    };

    // 如果T为多态类则value为true
    static const bool value = (sizeof(d2) == sizeof(d1));

    };

    // 当T并非类时采用这个版本
    template <class T>
    struct is_polymorphic_imp2{
    // 既然T不是类,那么就不存在多态,所以总是false
    static const bool value = false;
    };

    // 这个selector根据is_class的真假来选择判断的方式
    template <bool is_class>
    struct is_polymorphic_selector{
         // 如果is_class为false则由is_polymorphic_imp2来判断,这将导致结果总是false
         template <class T>
         struct rebind {
            typedef is_polymorphic_imp2<T> type; // 使用_imp2
        };
    };

    //当is_class为true时使用该特化版本
    template <>
    struct is_polymorphic_selector<true> // #7
    {
         // 如果is_class为true,则由is_polymorphic_imp1<>来作判断
         template <class T>
         struct rebind{
               typedef is_polymorphic_imp1<T> type; // 使用_imp1
         };
    };

    // is_polymorphic完全由它实现
    template <class T>
    struct is_polymorphic_imp{

    // 选择selector
            typedef is_polymorphic_selector<is_class<T>::value> selector; // #6
            typedef typename selector::template rebind<T> binder; // #8
            typedef typename binder::type imp_type; // #9
            static const bool value = imp_type::value;
    };

    注解
    #6处如果T为类,则is_class<T>::value为true,则那一行实际上就是:
    typedef is_polymorphic_selector<true> selector;
    这将决议为is_polymorphic_selector的第二个重载版本#7,其中的template rebind将判断的任务交给is_polymorphic_imp1,所以#8行的binder其实就是is_polymorphic_selector<true>::rebind<T>。而#9行的imp_type其实就是is_polymorphic_imp1<T>,结果正如预期。如果T不是类,按照类似的推导过程,最终会推导至is_polymorphic_imp2<T>::value,这正是false。

    “嗨!这太烦琐了!”你抱怨道:“可以简化!”。我知道,你可能会想到使用boost::ct_if(ct_if是?:三元操作符的编译期版本,像这样使用:

    typedef
    ct_if<CompileTimeBool,TypeIfTrue,TypeIfFalse>::value result;

    则当CompileTimeBool为true时result为TypeIfTrue,否则result为TypeIfFalse。ct_if<>的实现很简单,模板偏特化而已)。于是你这样写:

    typedef typename boost::ct_if<
    is_class<T>::value,
    is_polymorphic_imp1<T>,
    is_polymorphic_imp2<T>,
    >::type imp_type;

    static const bool value = imp_type::value;

    这在我的VC7.0环境下的确编译通过并正常工作,但是有一个小问题:假如T不是class,比如,T是一个int,则编译器的类型推导会将is_polymorphic_imp1<int>赋给ct_if的第二个模板参数,在这个过程中编译器会不会实例化is_polymorphic_imp1<int>(或者,换句话说,编译器会不会去查看它的定义)呢?如果实例化了,那么其内部的struct d1 : public ncvT会不会也跟着实例化为struct d1:public int,如果是这样,那么将会有编译期错误,因为C++标准不允许有public int这样的东西出现。事实上我的编译器没有报错,即是说它并没有去查看is_polymorphic_imp1<int>的定义。

    而C++标准实际上也支持这种做法。但boost库中的做法更为保险,也许是为了应付一些老旧的编译器。
    类型属性traits还有:alignment_of is_const is_volatile is_pod has_trivial_constructor等

  • 类型间关系

    is_base_and_derived(boost/type_traits/is_base_and_derived.hpp)

    定义
    template<typename B, typename D>
    struct bd_helper{
    template<typename T>
    static type_traits::yes_type check(D const volatile *, T);
    static type_traits::no_type check(B const volatile *, int);
    };

    template<typename B, typename D>
    struct is_base_and_derived_impl2{
    struct Host{
    // 该转换操作符当对象为const对象时才起作用
    operator B const volatile *() const;
    operator D const volatile *();
    };

    static const bool value = sizeof(bd_helper<B,D>::check(Host(), 0)) // #10
    == sizeof(type_traits::yes_type);

    };

    以上就是is_base_and_derived的底层机制。下面我就为你讲解它所仰赖的机制,假设有这样的类继承体系:
    struct B {};
    struct B1 : B {};
    struct B2 : B {};
    struct D : private B1, private B2 {};

    将D*转换为B1*会导致访问违规,因为私有基类部分无法访问,但是后面解释了这为什么不会发生。

    首先来看一些术语:
    SC - Standard Conversion
    UDC - User-Defined Conversion
    一个user-defined转换序列由一个SC后跟一个UDC后再跟一个SC组成。其中头尾两个SC都可以为到自身的转换(如:D->D),#10处将一个缺省构造的Host()交给bd_helper<B,D>::check函数。对于static no_type check(B const volatile *, int),我们有如下可行的隐式转换序列:

    Host -> Host const -> B const volatile* (UDC)

    Host -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)

    而对于static yes_type check(D const volatile *, T),我们则有如下转换序列:
    Host -> D const volatile* (UDC)

    C++标准说,在重载决议中选择最佳匹配函数时,只考虑标准转换(SC)序列,而这个序列直到遇到一个UDC为止,对于第一个函数,将Host -> Host const与Host -> Host比较,显然选择后者。因为后者是前者的一个真子集。因此,去掉第一个转换序列我们得到:

    C -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)
    vs.
    C -> D const volatile* (UDC)

    这里采用选择最短序列的原则,选择后者,这表明编译器甚至根本不需要去考虑向B转换的多重路径,或者访问限制,所以转换二义性和访问违规也就不会发生。结论是如果D继承自B,则选择yes_type check()。
    如果D不是继承自B,则对于static no_type check(B const volatile *, int)编译器的给出的转换为:
    C -> C const -> B const volatile*(UDC)
    对于static yes_type check(D const volatile *, T)编译器给出:
    C -> D const volatile* (UDC)
    这两个都不错(都需要一个UDC),然而由于static no_type check(B const volatile *, int)为非模板函数,所以被编译器选用。结论是如果D并非继承自B,则选择no_type check()。

    另外,在我的VC7.0环境下,如果将Host的operator B const volatile *() const的const拿掉,则结果将总是false。可惜这样的理解并不属于我,它们来自boost源代码中的注释。

    is_convertible(boost/type_traits/is_convertible.hpp)

    定义
    template< typename From >
    struct does_conversion_exist{
          template< typename To >
          struct result_{
              // 当不存在从From到To的任何转型时调用它
              static no_type _m_check(...);
              // 只要转型存在就调用它
              static yes_type _m_check(To);
              // 这只是个声明,所以并不占用空间,且没有开销。
              static From _m_from;
              enum {
                  value = sizeof( _m_check(_m_from) ) == sizeof(yes_type);
              };
         };
    };

    // 这是个为void准备的特化版本,因为不能声明void _m_from,只有void可以向void“转换”

    template<>
    struct does_conversion_exist<void>{
    template< typename To >
       struct result_{
          enum { value = ::boost::is_void<To>::value };
       };
    };

    // is_convertible完全使用does_conversion_exist作底层机制,所以略去。

    注解
    does_conversion_exist也使用了与is_class_impl一样的技术。所以注解从略。该技术最初由Andrei Alexandrescu发明。

    最后,Transformations Between Types(类型间转换),Synthesizing Types(类型合成),Function Traits(函数traits)的机制较为单纯,请自行参考boost提供的文档或头文件。

    traits是泛型世界中的精灵:小巧,精致。traits也是泛型编程中最精微的东西,它们往往仰赖于一些编译期决议的规则,C++标准,和神奇的模板偏特化。这也导致了它们在不同的平台上可能有不同表现,更常见的是,在某些平台上根本无法工作。然而,由于它们的依据是C++标准,而编译器会越来越符合标准,所以这些问题只是暂时的。traits也是构建泛型世界的基本组件之一,它们往往能使设计变得优雅,精致,甚至完美。

3/5/2009

刺猬

hedgehog

Hedgehogs are like prickly balls. 金山词霸上的例句:)

ciwei1

c++ traits study

Read sth about c++ traits, see the simple example below:

The meanning of the example: A zoo accepts abandoned dogs but no cats are welcomed.

traits

See more details about c++ traits:

(1) boost源码剖析之:泛型编程精灵type_traits

(2) Use Traits Classes for Information About Types

优化c++程序性能的一个小工具

以前都是用重量级的vtune,今天突然看到gprof这么个非常简单的工具,自己太逊了啊。

以下是一个简单的教程。

http://www.tongyi.net/develop/C/1057946.html

 

Best regards,

Xie,  Tao

 

3/4/2009

商人过河问题

3个商人带着3个仆人过河,过河的工具只有一艘小船,只能同时载两个人过河,包括划船的人。在河的任何一边,只要仆人的数量超过商人的数量,仆人就会联合起来将商人杀死并抢夺其财物,问应如何设计过河顺序才能让所有人都安全地过到河的另一边。  

网上看到一个解答,学习了一下:  

#include <stdio.h>

// 5 skemes to cross the river: 1 salesman, 1 servant, 2 salesman, 2 servant, 1 salesman and 1 servant.

char sln[5] = { ( 1 << 4 ), 1, ( 2 << 4 ), 2, ( 1 << 4 ) | 1 };
unsigned char curSln = 1;
char oldLayout[200][4], cur[200];  
// Here b indicates going to the opposite bank or getting back.
void DoSolution( char b ) { 
    unsigned long oldSln = curSln - 1; 
    oldLayout[ curSln ][0] = oldLayout[ oldSln ][0] - b * ( ( sln[ cur[ curSln ] ] & 0xF0 ) >> 4 ); 
    oldLayout[ curSln ][1] = oldLayout[ oldSln ][1] - b * ( sln[ cur[ curSln ] ] & 0xF ); 
    oldLayout[ curSln ][2] = oldLayout[ oldSln ][2] + b * ( ( sln[ cur[ curSln ] ] & 0xF0 ) >> 4 ); 
    oldLayout[ curSln ][3] = oldLayout[ oldSln ][3] + b * ( sln[ cur[ curSln ] ] & 0xF );
}

bool BeRepeated( char b ) { 
    for( unsigned long i = 0; i < curSln; i++ ) 
        if( oldLayout[ curSln ][0] == oldLayout[ i ][0] && 
                oldLayout[ curSln ][1] == oldLayout[ i ][1] && 
                oldLayout[ curSln ][2] == oldLayout[ i ][2] && 
                oldLayout[ curSln ][3] == oldLayout[ i ][3] && 
                ( ( i & 1 ) ? 1 : -1 ) == b ) // go or back must be same 
                // i&1 equals i%2; i&7 equals i%8, so does i&63 and i%64. 
            return true; 
    return false;
}

int main() { 
    char b = 1; 
    oldLayout[0][0] = oldLayout[0][1] = 3; 
    cur[0] = oldLayout[0][2] = oldLayout[0][3] = 0; 
    for( unsigned char i = 0; i < 200; i++ )      cur[ i ] = 0;  

    do   { 
        DoSolution( b ); 
        if( ( oldLayout[ curSln ][1] > oldLayout[ curSln ][0] && oldLayout[ curSln ][0] ) || 
                ( oldLayout[ curSln ][3] > oldLayout[ curSln ][2] && oldLayout[ curSln ][2] ) || 
                oldLayout[ curSln ][0] < 0 || oldLayout[ curSln ][1] < 0 || 
                oldLayout[ curSln ][2] < 0 || oldLayout[ curSln ][3] < 0 || 
                BeRepeated( b ) )      { 
              // rechoose a scheme...
P: 
               cur[ curSln ]++; 
               if( cur[ curSln ] > 4 )   { 
                    b = -b; 
                 cur[ curSln ] = 0; 
                 curSln--; 
                    if( !curSln )  break; // no answer to this question. 
                    goto P; 
               } 
              continue; 
         } 
        b = -b; 
        curSln++; 
    } 
    while( !( oldLayout[ curSln - 1 ][0] == 0 && oldLayout[ curSln - 1 ][1] == 0 && oldLayout[ curSln - 1 ][2] == 3 && oldLayout[ curSln - 1 ][3] == 3 ) ); 

    for(unsigned char  i = 0; i < curSln; i++ ) 
        printf( "%d %d\t %d %d\n", oldLayout[ i ][0],  oldLayout[ i ][1], oldLayout[ i ][2],  oldLayout[ i ][3] ); 
    return 0;
}

输出如下:
3 3      0 0
3 1      0 2
3 2      0 1
3 0      0 3
3 1      0 2
1 1      2 2
2 2      1 1
0 2      3 1
0 3      3 0
0 1      3 2
1 1      2 2
0 0      3 3

Best regards,

Xie,  Tao

(+8621)61166097  

Karl Max Has Seen It All - 《资本论》1867

 “Owners of capital will stimulate working class to buy more and more of expensive goods, houses and technology, pushing them to take more and more expensive credits, until their debt becomes unbearable. The unpaid debt will lead to bankruptcy of banks, which will have to be nationalized,and State will have to take the road which will eventually lead to communism.”
Karl Marx, 1867


资本家会刺激工人阶级大量借贷消费越来越贵的商品,房屋和新技术,这将促使他们背负越来越大的债务,直到这些债务无法偿还并导致银行破产,随后政府将对银行进行国有化,从而最终走向共产主义之路。 卡尔 马克思 《资本论》1867.

 

 

3/3/2009

why virtual template function is not allowed?

 

Guys, have you ever thought about this question?

I was wondering if it's possible to have a virtual template function. That is have a class, that has as one of its member functions a templated function that is also pure virtual.

ie.

Class A {

template <class T>
virtual int foo ( T & param ) = 0;
}

// class B is dervied from class A
Class B : public class A {

template <class T>
int foo (T & param) {
// implement foo...

}

}

 

Hints provided by another guru:

Graham

February 23rd, 2003, 07:37 AM

Think about it for a while: how could you make a templated virtual function? What's its signature? How many vtable entries do you reserve? How would you distinguish between an override/hide and an overload?

 

But, though c++ dose not allow virtual template function, you can also implement this function using boost::any, search for boos::any J.

Best regards,

Xie,  Tao

 

代码自动生成-宏带来的奇技淫巧

Author : Kevin Lynx

众多C++书籍都忠告我们C语言宏是万恶之首,但事情总不如我们想象的那么坏,就如同goto一样。宏有
一个很大的作用,就是自动为我们产生代码。如果说模板可以为我们产生各种型别的代码(型别替换)
那么宏其实可以为我们在符号上产生新的代码(即符号替换、增加)

关于宏的一些语法问题,可以在google上找到。相信我,你对于宏的了解绝对没你想象的那么多。如果你
还不知道###,也不知道prescan,那么你肯定对宏的了解不够。

我稍微讲解下宏的一些语法问题(说语法问题似乎不妥,macro只与preprocessor有关,跟语义分析又无关)

1. 宏可以像函数一样被定义,例如:
   #define min(x,y) (x<y?x:y) //
事实上这个宏存在BUG
  
但是在实际使用时,只有当写上min(),必须加括号,min才会被作为宏展开,否则不做任何处理。
  
2.
如果宏需要参数,你可以不传,编译器会给你警告(宏参数不够),但是这会导致错误。如C++书籍中所描
  
述的,编译器(预处理器)对宏的语法检查不够,所以更多的检查性工作得你自己来做。

3. 很多程序员不知道的###
   #
符号把一个符号直接转换为字符串,例如:
   #define STRING(x) #x
   const char *str = STRING( test_string ); str
的内容就是"test_string",也就是说#会把其后的符号
  
直接加上双引号。
   ##
符号会连接两个符号,从而产生新的符号(词法层次),例如:
   #define SIGN( x ) INT_##x
   int SIGN( 1 );
宏被展开后将成为:int INT_1;

4. 变参宏,这个比较酷,它使得你可以定义类似的宏:
   #define LOG( format, ... ) printf( format, __VA_ARGS__ )
   LOG( "%s %d", str, count );
   __VA_ARGS__
是系统预定义宏,被自动替换为参数列表。

5. 当一个宏自己调用自己时,会发生什么?例如:
   #define TEST( x ) ( x + TEST( x ) )
   TEST( 1 );
会发生什么?为了防止无限制递归展开,语法规定,当一个宏遇到自己时,就停止展开,也就是
  
说,当对TEST( 1 )进行展开时,展开过程中又发现了一个TEST,那么就将这个TEST当作一般的符号。TEST(1)
  
最终被展开为:1 + TEST( 1)

6. 宏参数的prescan
  
当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(有例外,见下文)。当展开后的宏参数被放进宏体时,
  
预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:
   #define PARAM( x ) x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) );
  
因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM
  
  
例外情况是,如果PARAM宏里对宏参数使用了###,那么宏参数不会被展开:
   #define PARAM( x ) #x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) );
将被展开为"ADDPARAM( 1 )"

   使用这么一个规则,可以创建一个很有趣的技术:打印出一个宏被展开后的样子,这样可以方便你分析代码:
   #define TO_STRING( x ) TO_STRING1( x )
   #define TO_STRING1( x ) #x
   TO_STRING
首先会将x全部展开(如果x也是一个宏的话),然后再传给TO_STRING1转换为字符串,现在你可以这样:
   const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );
去一探PARAM展开后的样子。

7. 一个很重要的补充:就像我在第一点说的那样,如果一个像函数的宏在使用时没有出现括号,那么预处理器只是
  
将这个宏作为一般的符号处理(那就是不处理)


我们来见识一下宏是如何帮助我们自动产生代码的。如我所说,宏是在符号层次产生代码。我在分析Boost.Function
模块时,因为它使用了大量的宏(宏嵌套,再嵌套),导致我压根没看明白代码。后来发现了一个小型的模板库ttl,说的
是开发一些小型组件去取代部分Boost(这是一个好理由,因为Boost确实太大)。同样,这个库也包含了一个function库。
这里的function也就是我之前提到的functorttl.function库里为了自动产生很多类似的代码,使用了一个宏:

#define TTL_FUNC_BUILD_FUNCTOR_CALLER(n)  \
 template< typename R, TTL_TPARAMS(n) > \
 struct functor_caller_base##n \
        ///...
该宏的最终目的是:通过类似于TTL_FUNC_BUILD_FUNCTOR_CALLER(1)的调用方式,自动产生很多functor_caller_base模板:
template <typename R, typename T1> struct functor_caller_base1
template <typename R, typename T1, typename T2> struct functor_caller_base2
template <typename R, typename T1, typename T2, typename T3> struct functor_caller_base3
///...
那么,核心部分在于TTL_TPARAMS(n)这个宏,可以看出这个宏最终产生的是:
typename T1
typename T1, typename T2
typename T1, typename T2, typename T3
///...
我们不妨分析TTL_TPARAMS(n)的整个过程。分析宏主要把握我以上提到的一些要点即可。以下过程我建议你翻着ttl的代码,
相关代码文件:function.hpp, macro_params.hpp, macro_repeat.hpp, macro_misc.hpp, macro_counter.hpp

so, here we go

分析过程,逐层分析,逐层展开,例如TTL_TPARAMS(1)

#define TTL_TPARAMS(n) TTL_TPARAMSX(n,T) 
=> TTL_TPARAMSX( 1, T )
#define TTL_TPARAMSX(n,t) TTL_REPEAT(n, TTL_TPARAM, TTL_TPARAM_END, t)
=> TTL_REPEAT( 1, TTL_TPARAM, TTL_TPARAM_END, T )
#define TTL_TPARAM(n,t) typename t##n,
#define TTL_TPARAM_END(n,t) typename t##n
#define TTL_REPEAT(n, m, l, p) TTL_APPEND(TTL_REPEAT_, TTL_DEC(n))(m,l,p) TTL_APPEND(TTL_LAST_REPEAT_,n)(l,p)
注意,TTL_TPARAM, TTL_TPARAM_END虽然也是两个宏,他们被作为TTL_REPEAT宏的参数,按照prescan规则,似乎应该先将
这两个宏展开再传给TTL_REPEAT。但是,如同我在前面重点提到的,这两个宏是function-like macro,使用时需要加括号,
如果没加括号,则不当作宏处理。因此,展开TTL_REPEAT时,应该为:
=> TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T) TTL_APPEND( TTL_LAST_REPEAT_,1)(
TTL_TPARAM_END,T)
这个宏体看起来很复杂,仔细分析下,可以分为两部分:
TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T)
以及
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
先分析第一部分:
#define TTL_APPEND( x, y ) TTL_APPEND1(x,y) //
先展开x,y再将x,y连接起来
#define TTL_APPEND1( x, y ) x ## y
#define TTL_DEC(n) TTL_APPEND(TTL_CNTDEC_, n)
根据先展开参数的原则,会先展开TTL_DEC(1)
=> TTL_APPEND(TTL_CNTDEC_,1) => TTL_CNTDEC_1
#define TTL_CNTDEC_1 0 
注意,TTL_CNTDEC_不是宏,TTL_CNTDEC_1是一个宏。
=> 0
也就是说,TTL_DEC(1)最终被展开为0。回到TTL_APPEND部分:
=> TTL_REPEAT_0 (TTL_TPARAM,TTL_TPARAM_END,T)
#define TTL_REPEAT_0(m,l,p)
TTL_REPEAT_0
这个宏为空,那么,上面说的第一部分被忽略,现在只剩下第二部分:
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
=> TTL_LAST_REPEAT_1 (TTL_TPARAM_END,T) // TTL_APPEND
TTL_LAST_REPEAT_1合并起来
#define TTL_LAST_REPEAT_1(m,p) m(1,p)
=> TTL_TPARAM_END( 1, T )
#define TTL_TPARAM_END(n,t) typename t##n
=> typename T1 
展开完毕。

虽然我们分析出来了,但是这其实并不是我们想要的。我们应该从那些宏里去获取作者关于宏的编程思想。很好地使用宏
看上去似乎是一些偏门的奇技淫巧,但是他确实可以让我们编码更自动化。

参考资料:
macro
语法: http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/cpp/Macros.html
ttl(tiny template library) : http://tinytl.sourceforge.net/

 

 

Best regards,

Xie,  Tao

(+8621)61166097

 

 

涛 谢

Occupation

welcome :)

算算活了多少天

Loading...

Video

No content has been added yet.
Photo 1 of 39