• [转]boost.bind 如何 bind continue.1


    我们先定义目标:

    1. simple_bind
    提供与 bind 类似的界面,可以只考虑通过对象引用(或者值)调用成员函数的情况,而不考虑 free function 或者通过指针调用等等。具体地说,就是允许

    person.SetName("Ralph")      --> simple_bind(&Person::SetName, person, _1)(string("Ralph"))
                                     simple_bind(&Person::SetName, _1, string(“Ralph"))(person)
                                     simple_bind(&Person::SetName, _1, _2)(person, string("Ralph"))
                                     simple_bind(&Person::SetName, _2, _1)(string("Ralph"), person)


    2.
    一个 simple_bind 表达式会 evaluate 成为一个 simple_binder object ,这是一个 functor ,如:

    simple_bind(&Person::SetName, person, _1)(string("Ralph"))     --> simple_binder_obj(string("Ralph"))
    simple_bind(&Person::SetName, _1, _2)(person, string("Ralph")) --> simple_binder_obj(person, string(“Ralph”))


    在这一头一尾,我们其实没多少选择余地,一方面我们需要提供的用户界面已经定了,另一方面需要提供的函数调用界面也基本上固定了,要玩的 magic 也就是发生在中间。

    3.
    对于 placeholder ,前面已经说过,就用简单的解决方案:
    template <int I>
    class placeholder{};
    placeholder<1> _1;
    placeholder<2> _2;
    ...

    有了这3个出发点,现在从 simple_bind 出发,首先看看 simple_bind 应该有的形式。从前面的实验中可以看出

    template <typename R, typename T, typename Arg>

    simple_binder<R, T, Arg>

    simple_bind( R (T::*pfn)(Arg), const Arg& arg, placeholder&)

    {

        return simple_binder<R, T, Arg>(pfn, arg);

    }

    这样的形式虽然方便,但却不具有 scalability 。因为R (T::*pfn)(Arg) 的限制太大了,首先,它只针对接受一个参数的成员函数,如果要支持多个参数,你要对每个参数数量(它有一个术语叫 arity)写一个重载,而这些不同 arity 还要跟 simple_bind 本身的 arity 做组合,结果是爆炸性的 simple_bind 数量。其次,参数 arg placeholder 也太不灵活,前面已经看到,仅仅在两个参数的情况下,就有4种组合:

        simple_bind(&Person::SetName, _1, _2)(person, "Joy");

        simple_bind(&Person::SetName, _2, _1)("Joy", person);

        simple_bind(&Person::SetName, _1, "Joy")(person);

        simple_bind(&Person::SetName, person, _1)("Joy");

    可想而知参数更多的时候的恐怖情景。于是我们得出结论:


    由于前端的调用组合太复杂,simple_bind 的界面必须以最 generic 的方式给出,由此带来的复杂性应该留待 simple_bind 内部处理。

    这是经过了实验的教训得出的结论。

    何谓“最 generic”?没有比这更加 generic 的了:

    template <typename F, typename A1>

    simple_binder<unspecified>

    simple_bind(F f, A1 a1)

    {

        //...

    }

    即便如此,由于 C++ 至今还没有可变数量的模板参数,我们还是不能回避对每一个 arity 都有所定义,但是这已经把复杂度控制在 O(arity) 了:

    template <typename F, typename A1, typename A2>

    simple_binder<unspecified>

    simple_bind(F f, A1 a1, A2 a2)

    {

        //...

    }
    ......


    但是随之而来的就是问题:我现在把第一个参数(函数指针)类型笼统成了一个 F ,我如何知道它的返回类型、参数类型和对象类型?好在这个问题可解,其技法在 boost 里面还很常见,那就是 traits

    template <typename T>

    struct result_of

    {};

    template <typename R, typename T>

    struct result_of<R (T::*)()>

    {

        typedef R type;

    };

    template <typename R, typename T, typename A1>

    struct result_of<R (T::*)(A1)>

    {

        typedef R type;

    };

    template <typename R, typename T, typename A1, typename A2>

    struct result_of<R (T::*)(A1, A2)>

    {

        typedef R type;

    };


    ......


    它无非利用 C++ 模板元编程最常用的偏特化来“抽取”函数指针的类型内容,我们甚至无需自己写,boost 已经提供好了相应的工具,但是上面的这个 result_of 也还算清晰好用,在这个例子里面,我们就用它。

    在这个方向上,思路已经比较开阔了,现在从另一个方向:simple_binder 入手来想一想。simple_binder 应该长成什么样子呢?由于 function_traits 可以帮我们得到函数返回类型,我们可以肯定 simple_binder 在构造的时候已经知道返回类型了,那么它应该是这样吗?

    template <typename R/*
    返回值的类型*/, typename F, typename A1>

    class simple_binder

    template <typename R, typename F, typename A1, typename A2>

    class simple_binder

    如果你的想法是这样,恭喜你,一出门你就撞了南墙:class 不是 function ,除非你偏特化,它是不允许有多个模板参数“重载”的!但是很显然,这些 simple_binder 之间,谁也不是谁的偏特化。那么是不是可以利用类似 TypeList 的技术,用默认模板参数提供变相的可变参数数量模板:

    template <typename R, typename F, typename A1, typename A2 = placeholder<0>, typename A3 = placeholder<0> /*...*/ >

    class simple_binder
    ......


    好,这下子 simple_bind 的定义算是完整了:

    template <typename F, typename A1>

    simple_binder<typename result_of<F>::type, F, A1>

    simple_bind(F f, const A1& arg1)

    {

    //...

    }

    template <typename F, typename A1, typename A2>

    simple_binder<typename result_of<F>::type, F, A1, A2>

    simple_bind(F f, const A1& arg1, const A2& arg2)

    {

        //...

    }

    在这个基础上,提供 simple_binder 的构造函数也是举手之劳,

        explicit simple_binder(F f, const A1& a1)

            : f_(f)

            , a1_(a1)

        {}

       

        explicit simple_binder(F f, const A1& a1, const A2& a2)

            : f_(f)

            , a1_(a1)

            , a2_(a2)

        {}

    其中的 f_, a1_, a2_ 都是 simple_binder 的成员。simple_bind 仍然只是简单的构造一个 simple_binder

    template <typename F, typename A1>

    simple_binder<typename result_of<F>::type, F, A1>

    simple_bind(F f, const A1& arg1)

    {

        return simple_binder

            <

            typename result_of<F>::type,

            F,

            A1

            >

        (f, arg1);

    }

    template <typename F, typename A1, typename A2>

    simple_binder<typename result_of<F>::type, F, A1, A2>

    simple_bind(F f, const A1& arg1, const A2& arg2)

    {

        return simple_binder

            <

            typename result_of<F>::type,

            F,

            A1,

            A2

            >

        (f, arg1, arg2);

    }

    至于真正的运算,就留给 simple_binder 和它的助手们去做。我们仍然从最简单的情况开始,研究一下调用时没有参数是什么样,例如这样的调用:

    simple_bind(&Person::Name, person)()

    这个调用的参数在 simple_bind 的时候已经确定,而在 simple_binder 那里只是简单调用一个0参数的 operator(),也就是:


        R operator()()

        {

            //...

        }

    即便是这个东西,它代表的意义也不单纯,实际上,在有两个 placeholder 的场合,它代表了两种情况:

    1.
    类似 simple_bind(&Person::Name, person)(),函数实际上没有参数
    2.
    类似 simple_bind(&Person::SetName, person, “Joy”)(), 函数其实有一个参数

    我们有没有足够的信息来确定是那种情况呢?有,答案就在 simple_binder 的模板参数 A1, A2 中。如果 A2 placeholder<0> ,那么就是第一种情况,否则就是第二种情况。为了能在这两种情况之间做出决策,必须加上一个助手,我们称之为 eval

    template <typename R, typename F, typename A1, typename A2>

    R eval(F f, A1 a1, A2 a2)

    {

        return (a1.*f)(a2);

    }

    template <typename R, typename F, typename A1>

    R eval(F f, A1 a1, placeholder<0>)

    {

        return (a1.*f)();

    }

    于是乎0参数的 operator() 就成了

        R operator()()

        {

            return eval<R>(f_, a1_, a2_);

        }

    参数 a2_ 会帮助我们把调用分派到两个 eval 中的一个,从而完成调用决策。

    现在来考虑 1 个参数的 operator() ,它就复杂一些了:

    1.
    类似 bind(&Person::Name, _1)(person) ,函数其实没有参数
    2.
    类似 bind(&Person::SetName, _1, "Joy")(person),函数其实有1个参数,_1 代表第一个
    3.
    类似 bind(&Person::SetName, person, _1)("Joy"),函数其实有1个参数,_1 代表第二个

    停!!!看来在这个方向上鸣金收兵的时候到了。回顾一下这个设计:
    通过提供 generic simple_bind 界面以及 result_of ,我们简化了 simple_bind ;又通过 eval ,我们让 simple_binder 变得单纯起来,然而现在 eval 的复杂性开始膨胀,大有脱离我们控制的趋势。原因在哪里?

    参数的复杂性。

    作为一个系统,我们的这个东西其实接受两组参数,一组是由 simple_bind 接受的,混杂了 placeholder 和实际参数;另一组是由 simple_binder operator() 接受的,全部都是实际参数,但是其应用的顺序需要根据 placeholder 的类型来决定。了解了这个复杂性的根源,我们的任务再次变得清晰起来:提供一个方法,以统一的方式来处理参数,降低复杂性。
  • 相关阅读:
    电源
    SM2947
    网表
    cadence设计思路
    青山依旧在,几度夕阳红
    乐观锁与悲观锁
    笔记
    强弱软虚引用
    缓存相关
    dubbo
  • 原文地址:https://www.cnblogs.com/adylee/p/2068322.html
Copyright © 2020-2023  润新知