|
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
{
...
private:
class body
{
public:
virtual ~body_base();
virtual result_type call() = 0;
... // cloning or counting features, as necess
};
template<typename adaptee_type>
class adapter : public body
{
public:
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
____________________________________________________________
Kevlin Henney phone: +44 117 942 2990
Curbralan Limited mobile: +44 7801 073 508
mailto:kevlin_at_[hidden] fax: +44 870 052 2289
http://www.curbralan.com
____________________________________________________________
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk