Boost logo

Boost :

From: Kevlin Henney (kevlin_at_[hidden])
Date: 2000-12-06 12:36:45

In message <200012052146.NAA13370_at_[hidden]>, Karl Nelson
<kenelson_at_[hidden]> writes
>Well, in the cloned case because we can't have variable sized
>objects, inevitably we need an indirection on to the heap.
>Thus for the sake of argument lets assume that both sharing and
>cloning have a stack size of 1 pointer. Thus stack size is
>generally considered a wash.
>In the cloned case the heap size is the size of the composite object
>which is the size of all the adaptors plus the size of the generic
>functor plus the user supplied functor.

Let me just check we are talking about the same thing here, focusing on
a straight zero arg version:

        template<typename result_type>
        class any_function
                class body
                        virtual ~body_base();
                        virtual result_type call() = 0;
                        ... // cloning or counting features, as necess

                template<typename adaptee_type>
                class adapter : public body
                        virtual result_type call();
                        adaptee_type adaptee;

                body * callee;

The only difference between the two variants at this level is that
cloning requires one more vtable entry than the reference counting
version and the reference counting version, which uses a directly
counted body, requires extra space per body instance for a counter. They
are otherwise pretty much the same in terms of their impact on system
resources, with roughly the same code overhead and roughly the same
working set for a single instance. The difference becomes apparent with
copying, but the point is that basic structure is not dissimilar.

>> I'm not sure that I understand what you mean by operator= being
>> implemented lots. Both the reference-counted and cloning designs must
>> implement operator=, but in one case clone is called and in the other
>> the count is incremented. However, roughly the same amount of code --
>> which is small -- will be generated in each case for operator=.
>Okay let me explain this better.... assume we have 4 levels of indirection
>2 adaptor one of which introduced some objects, the generic functor, and
>then the user functor (a function ptr in this case).
>(in sigc++ terms, this is a rather extreme case)
> void foo(int,A);
> Slot1<int,int> s1=retbind(bind(slot(&foo),A(10)),1);
> Slot1<int,int> s2;
> s2=s1; // <-- this statement
>In the case of cloning we have to have an
> adp_rettype<int, adp_bind<A, func_functor<void,int,A> > >::copy
> adp_bind<A, func_functor<void,int,A>::copy
> A::copy
> func_functor<void,int,A>::copy
>where copy is either operator= or X::X(const X&) depending on the
>implementation. If I call this 1000 times in a row this would be
>significant. Further, every different set of adaptors and
>functors will generate a new set of assignment operator or copy
>constructor. (Basically this is a type explosion.)

Right, you are working to a different set of assumptions -- and indeed
quite a different design -- which is why we have arrived at different
answers. There is no difference in the number of assignment operators
generated (only one) between reference-counted and cloning versions in
the code I outlined above.


  Kevlin Henney phone: +44 117 942 2990
  Curbralan Limited mobile: +44 7801 073 508
  mailto:kevlin_at_[hidden] fax: +44 870 052 2289

Boost list run by bdawes at, gregod at, cpdaniel at, john at