C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Proposal: Improving TR1 Smart Pointers
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards
View previous topic :: View next topic  
Author Message
vladimir kliatchko
Guest





PostPosted: Wed Aug 10, 2005 6:44 am    Post subject: Proposal: Improving TR1 Smart Pointers Reply with quote



Abstract

Over a period of two years we have accumulated significant experience
in developing and then using a family of C++ components implementing
smart pointers. While similar to the smart pointers described by TR1,
our implementation has some important distinctions. Our experience
shows that these distinctions often resulted in both improved usability
and superior performance. In this paper we describe how our
implementation differs from TR1 and propose a number of corresponding
changes to the C++ standard.

To see the entire proposal, follow this link:
http://home.earthlink.net/~phalpern/cpp/KliatchkoSharedPtr.pdf

Sincerely,
--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Back to top
John Nagle
Guest





PostPosted: Thu Aug 11, 2005 5:26 am    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote



vladimir kliatchko wrote:

Quote:
Abstract

Over a period of two years we have accumulated significant experience
in developing and then using a family of C++ components implementing
smart pointers. While similar to the smart pointers described by TR1,
our implementation has some important distinctions. Our experience
shows that these distinctions often resulted in both improved usability
and superior performance. In this paper we describe how our
implementation differs from TR1 and propose a number of corresponding
changes to the C++ standard.

To see the entire proposal, follow this link:
http://home.earthlink.net/~phalpern/cpp/KliatchkoSharedPtr.pdf

It's hard to evaluate this. There are no examples, no
discussion of safety, and no indications of programmer error
rates or MTBF.

The notion of reference-counted pointers to portions of an
object is interesting, but seems overly complicated. Scoped
pointers might be a more effective way of offering safe access to
objects internal to another object.

John Nagle
Animats

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Thu Aug 11, 2005 4:00 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote



John Nagle wrote:
Quote:
vladimir kliatchko wrote:

Abstract

Over a period of two years we have accumulated significant experience
in developing and then using a family of C++ components implementing
smart pointers. While similar to the smart pointers described by TR1,
our implementation has some important distinctions. Our experience
shows that these distinctions often resulted in both improved usability
and superior performance. In this paper we describe how our
implementation differs from TR1 and propose a number of corresponding
changes to the C++ standard.

To see the entire proposal, follow this link:
http://home.earthlink.net/~phalpern/cpp/KliatchkoSharedPtr.pdf


It's hard to evaluate this. There are no examples, no
discussion of safety, and no indications of programmer error
rates or MTBF.


Let me provide an example which combines all of the proposed new
features and, in our experience, is not unrealistic:

class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<WorkItem>& workItem);
// note use of the managed_ptr in the interface:
// AsynchProcessor requires ownership of the
// item but does not make any assumptions about
// sharing this ownership with the caller.
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
itemsPtr.create_obj(pool); // housing a vector in the shared_ptr
// while also using the pool to optimize
// memory allocation performance
workStream >> *itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}

// end-of-the-example

I hope this example helps with the evaluation.

As far as programmer errors are concerned:
Our experience using all of the proposed features (allocator support,
collation of shared objects with the shared pointers, aliasing,
managed_ptrs) seems to indicate that while compile-time detectable
errors may be common for a programmer just introduced to these
features, harder to detect runtime errors are highly unlikely. Our
experience may not be very representative though.

Quote:
The notion of reference-counted pointers to portions of an
object is interesting, but seems overly complicated. Scoped
pointers might be a more effective way of offering safe access to
objects internal to another object.

The implementation of aliasing is very straightforward, so is the use. I
guess the concept itself may be advanced; but, then, it has zero cost
when not used and very difficult to do without in cases illustrated by
the example above.

Quote:

John Nagle
Animats

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Thu Aug 11, 2005 7:41 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

Howard Hinnant wrote:
Quote:
In article <_3KKe.3550$Gx1.2720 (AT) fe11 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:


class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<WorkItem>& workItem);
// note use of the managed_ptr in the interface:
// AsynchProcessor requires ownership of the
// item but does not make any assumptions about
// sharing this ownership with the caller.
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
itemsPtr.create_obj(pool); // housing a vector in the shared_ptr
// while also using the pool to optimize
// memory allocation performance
workStream >> *itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}


Thank you for the example use. Could you compare/contrast it with the
following?

class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(WorkItem& workItem);
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
std::vector<WorkItem, LockFreePoolAllocator> itemsPtr(pool);
workStream >> itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr.begin();
while (i!=itemsPtr.end())
{
processor.processWorkItem(*i);
++i;
}
}

Thanks,
Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


The modified version of the example above
AsynchProcessor::processWorkItem does not use any kind of smart pointer
and thus cannot acquire ownership of a work item. The original version
of the example used managed_ptr in the interface of AsyncProcessor to
demonstrate how ownership of an element of a vector can be passed
through an interface using aliasing and the conversion from shared_ptr
to managed_ptr. I used the name 'AsynchProcessor' to suggest
(unsuccessfully, sorry, I should have been more explicit) that the
processing of work items is done asynchronously and therefore may not
complete in time for the next iteration of the loop. The asynchronous
nature of AsynchProcessor::processWorkItem was supposed to make the
transfer of ownership essential.
I hope this explanation helps. Pls let me know if I am still not
illustrating the proposed features clearly.
--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Howard Hinnant
Guest





PostPosted: Thu Aug 11, 2005 7:49 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

In article <_3KKe.3550$Gx1.2720 (AT) fe11 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:

Quote:
class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<WorkItem>& workItem);
// note use of the managed_ptr in the interface:
// AsynchProcessor requires ownership of the
// item but does not make any assumptions about
// sharing this ownership with the caller.
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
itemsPtr.create_obj(pool); // housing a vector in the shared_ptr
// while also using the pool to optimize
// memory allocation performance
workStream >> *itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}

Thank you for the example use. Could you compare/contrast it with the
following?

class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(WorkItem& workItem);
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
std::vector<WorkItem, LockFreePoolAllocator> itemsPtr(pool);
workStream >> itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr.begin();
while (i!=itemsPtr.end())
{
processor.processWorkItem(*i);
++i;
}
}

Thanks,
Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Howard Hinnant
Guest





PostPosted: Fri Aug 12, 2005 1:18 am    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

In article <HANKe.26697$sf6.247 (AT) fe08 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:

Quote:
The modified version of the example above
AsynchProcessor::processWorkItem does not use any kind of smart pointer
and thus cannot acquire ownership of a work item. The original version
of the example used managed_ptr in the interface of AsyncProcessor to
demonstrate how ownership of an element of a vector can be passed
through an interface using aliasing and the conversion from shared_ptr
to managed_ptr. I used the name 'AsynchProcessor' to suggest
(unsuccessfully, sorry, I should have been more explicit) that the
processing of work items is done asynchronously and therefore may not
complete in time for the next iteration of the loop. The asynchronous
nature of AsynchProcessor::processWorkItem was supposed to make the
transfer of ownership essential.
I hope this explanation helps. Pls let me know if I am still not
illustrating the proposed features clearly.

I did miss that point completely. Thanks for the clarification.
Requoting your original code to continue disscussion:

class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<WorkItem>& workItem);
// note use of the managed_ptr in the interface:
// AsynchProcessor requires ownership of the
// item but does not make any assumptions about
// sharing this ownership with the caller.
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
itemsPtr.create_obj(pool); // housing a vector in the shared_ptr
// while also using the pool to optimize
// memory allocation performance
workStream >> *itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}

Is it now true that LockFreePoolAllocator has to deal with simultaneous
deallocations from multiple threads? That's not necessarily a bad
thing, just I didn't expect it because of the name.

Shouldn't processWorkItem take the managed_ptr by value? Otherwise the
temp generated at the call site won't bind to the non-const ref.

What is the advantage of using managed_ptr over shared_ptr in the
processWorkItem interface? In this context, IIUC, managed_ptr shares
ownership with the shared_ptr's in the keepGoing loop. So it must take
into account that if it modifies *workItem, others may see the change.
Otoh, if processWorkItem is designed to uniquely own the workItem
(making changes to it without worrying what other's see), perhaps
transfer of ownership (instead of sharing ownership) to processWorkItem
might be preferable.

I've been working on something called unique_ptr that is like your
managed_ptr, except that it typically has the same overhead as auto_ptr
(for empty custom deleters). And it won't share ownership with
shared_ptr (or anything else). Its use in your example would transfer
instead of share ownership and might look something like:

class LockFreePoolAllocator;
class WorkItem;

typedef std::unique_ptr<WorkItem, LockFreePoolAllocator> Ptr;

class AsynchProcessor {
void processWorkItem(Ptr workItem);
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
std::vector<Ptr, LockFreePoolAllocator> itemsPtr(pool);
workStream >> itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr.begin();
while (i!=itemsPtr.end())
{
processor.processWorkItem(std::move(*i));
++i;
}
}

Here the memory ownership is explicit transferred to processWorkItem
with the move() function. Assuming the unique_ptr's are pointing into
pool, LockFreePoolAllocator still has to deal with simultaneous
deallocations though. Or perhaps they could point into thread-specific
pools, and then truly get rid of the need for a thread-aware pool.

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Howard Hinnant
Guest





PostPosted: Fri Aug 12, 2005 5:05 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

In article <_3KKe.3550$Gx1.2720 (AT) fe11 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:

Quote:
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing

I've been looking at other ways this type of aliasing can be achieved
with the existing tr1::shared_ptr spec. Fwiw, here's one possibility:

class link_life_to
{
typedef std::shared_ptr<void> D;
public:
link_life_to(const D& d) : d_(d) {}

template <class T> void operator()(T*) {}
private:
D d_;
};

Now you can say:

shared_ptr<WorkItem> item(&*i, link_life_to(itemsPtr)); // aliasing

I believe this has the same effect as the proposed shared_ptr
constructor. A disadvantage though is that it sets up a second
reference count instead of reusing the original reference count. But
there are a couple of advantages.

One advantage is that it is easier to search for lifetime dependencies
between otherwise unrelated shared_ptr's (search for "link_life_to").

Another advantage is that the approach is extendable to any smart
pointer that takes a custom deleter. For example you could use this
tool to make unique_ptr behave like the proposed managed_ptr in that it
would share ownership with a shared_ptr:

typedef unique_ptr<WorkItem, link_life_to> UPtr;
processor.processWorkItem(Uptr(&*i, link_life_to(itemsPtr)));

The resulting unique_ptr actually has a lower overhead compared to
managed_ptr in my experiments (sizeof == 3 words instead of 4 words).

If link_life_to is templated:

template <class D = std::shared_ptr
class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}

template <class T> void operator()(T*) {}
private:
D d_;
};

then it can used to link the lifetimes of other types of smart pointers
(as long as they accept custom deleters). In order to use a unique_ptr
as the lifetime to link with, link_life_to needs to be extended to
handle move-only types:

template <class D = std::shared_ptr
class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}
link_life_to(D&& d) : d_(std::move(d)) {}
link_life_to(link_life_to&& x) : d_(std::move(x.d_)) {}
link_life_to& operator=(link_life_to&& x)
{d_ = std::move(x.d_); return *this;}

template <class T> void operator()(T*) {}
private:
D d_;
};

Unless of course move members are auto-generated as Mirek Fidler
proposes, in which case you don't need the explicit move constructor or
move assignment.

Or perhaps with N1717 (extended):

template <class D = std::shared_ptr
class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}
link_life_to(D&& d) : d_(std::move(d)) {}
link_life_to(link_life_to&&) {default}
link_life_to& operator=(link_life_to&&) {default}

template <class T> void operator()(T*) {}
private:
D d_;
};

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Fri Aug 12, 2005 5:40 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

Howard Hinnant wrote:
Quote:
In article <HANKe.26697$sf6.247 (AT) fe08 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:

The modified version of the example above
AsynchProcessor::processWorkItem does not use any kind of smart pointer
and thus cannot acquire ownership of a work item. The original version
of the example used managed_ptr in the interface of AsyncProcessor to
demonstrate how ownership of an element of a vector can be passed
through an interface using aliasing and the conversion from shared_ptr
to managed_ptr. I used the name 'AsynchProcessor' to suggest
(unsuccessfully, sorry, I should have been more explicit) that the
processing of work items is done asynchronously and therefore may not
complete in time for the next iteration of the loop. The asynchronous
nature of AsynchProcessor::processWorkItem was supposed to make the
transfer of ownership essential.
I hope this explanation helps. Pls let me know if I am still not
illustrating the proposed features clearly.

I did miss that point completely. Thanks for the clarification.
Requoting your original code to continue disscussion:

class LockFreePoolAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<WorkItem>& workItem);
// note use of the managed_ptr in the interface:
// AsynchProcessor requires ownership of the
// item but does not make any assumptions about
// sharing this ownership with the caller.
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
itemsPtr.create_obj(pool); // housing a vector in the shared_ptr
// while also using the pool to optimize
// memory allocation performance
workStream >> *itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}


Is it now true that LockFreePoolAllocator has to deal with simultaneous
deallocations from multiple threads? That's not necessarily a bad
thing, just I didn't expect it because of the name.

I used the name 'LockFreePoolAllocator' to suggest a high-performance
('lock-free' part was supposed to carry this suggestion) thread-safe
allocator to illustrate how we use allocators with smart pointer (see
the paper for more info).

Quote:

Shouldn't processWorkItem take the managed_ptr by value? Otherwise the
temp generated at the call site won't bind to the non-const ref.

Yes, it should. My bug.

Quote:

What is the advantage of using managed_ptr over shared_ptr in the
processWorkItem interface? In this context, IIUC, managed_ptr shares
ownership with the shared_ptr's in the keepGoing loop. So it must take
into account that if it modifies *workItem, others may see the change.
Otoh, if processWorkItem is designed to uniquely own the workItem
(making changes to it without worrying what other's see), perhaps
transfer of ownership (instead of sharing ownership) to processWorkItem
might be preferable.


We generally prefer managed_ptr in interfaces like processWorkItem
because while such interfaces allow callers to pass in shared_ptrs, the
callers who do not need to retain ownership are allowed to pass
managed_ptrs directly. Passing managed_ptrs is significantly more
efficient then passing shared_ptrs.
Also, using managed_ptr in the interface makes a statement regarding
who controls the lifetime of the object but not regarding who is
allowed to modify it. This can be separately expressed by using (or not
using) the const qualifier (e.g. managed_ptr<const WorkItem>). Note
that using share_ptr in an interface conveys no more information on who
is allowed to modify the shared object. (For more on advantages of
using managed_ptr as an interface type see the paper)

Quote:
I've been working on something called unique_ptr that is like your
managed_ptr, except that it typically has the same overhead as auto_ptr
(for empty custom deleters). And it won't share ownership with
shared_ptr (or anything else). Its use in your example would transfer
instead of share ownership and might look something like:

class LockFreePoolAllocator;
class WorkItem;

typedef std::unique_ptr<WorkItem, LockFreePoolAllocator> Ptr;

class AsynchProcessor {
void processWorkItem(Ptr workItem);
};

LockFreePoolAllocator pool;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
std::vector<Ptr, LockFreePoolAllocator> itemsPtr(pool);
workStream >> itemsPtr;
std::vector<WorkItem>::iterator i = itemsPtr.begin();
while (i!=itemsPtr.end())
{
processor.processWorkItem(std::move(*i));
++i;
}
}

Here the memory ownership is explicit transferred to processWorkItem
with the move() function. Assuming the unique_ptr's are pointing into
pool, LockFreePoolAllocator still has to deal with simultaneous
deallocations though. Or perhaps they could point into thread-specific
pools, and then truly get rid of the need for a thread-aware pool.

Requirement to use std::vector<WorkItem> rather then a vector of smart
pointers was important in my example to illustrate the use of aliasing.
This requirement may be motivated by performance considirations, by the
need for compatibility with a particular wire format or a legacy
interface, or by that fact that std::vector<WorkItem> is eaier to
marshall accross process boundries.

It is also important for our implementation of the support for
allocators that neither managed_ptr class nor shared_ptr class are
templatized on the type of the allocator. It seems that the unique_ptr
is.

Also, I am not familiar with the move function. Where can I find some
info on it?

Quote:

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Updated example:

class HighPerformanceThreadSafeAllocator;
class WorkItem;

class AsynchProcessor {
void processWorkItem(managed_ptr<const WorkItem> workItem);
// Process work item in a separate thread.
// Note use of the managed_ptr in the
// interface: AsynchProcessor requires
// ownership of the item but does not make
// any assumptions about sharing this
// ownership with the caller.
};

HighPerformanceThreadSafeAllocator alloc;
AsynchProcessor processor

// performance critical item processing loop
while (keepGoing)
{
shared_ptr<std::vector itemsPtr;
// must be std::vector<WorkItem>
// for performance and
// "wire format compatibility"

itemsPtr.create_obj(alloc); // housing a vector in the shared_ptr
// while also using the pool to
optimize
// memory allocation performance

int numWorkItems;
workStream >> numWorkItems;
itemsPtr->reserve(numWorkItems);
ReadWorkItems(workStream, *itemsPtr, numWorkItems);

std::vector<WorkItem>::iterator i = itemsPtr->begin();
while (i!=itemsPtr->end())
{
shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing
processor.processWorkItem(item); // share to managed conversion
++i;
}
}

// end-of-the-example

--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Howard Hinnant
Guest





PostPosted: Fri Aug 12, 2005 6:17 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

In article <1123862967.412092.115540 (AT) g47g2000cwa (DOT) googlegroups.com>,
"vladimir kliatchko" <vladimir (AT) kliatchko (DOT) com> wrote:

Thanks for the answers and updated example.

Quote:
Also, I am not familiar with the move function. Where can I find some
info on it?

Oh, sorry. I've been working a proposal for the past 3-4 years and
sometimes I get so deep into it I start assuming knowledge of it in my
discussions. ;-)

In reverse chronological order:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1770.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

In a nutshell: These papers introduce a new type of reference called an
rvalue-reference. The rvalue-reference is very similar to our current
reference (now named lvalue-reference) except that it will bind to
rvalues even when not const-qualifed. Overload resolution rules have
been carefully crafted between the lvalue and rvalue references so as to
enable both "move semantics" and "perfect forwarding".

The proposed std::move does nothing but cast an lvalue to an rvalue.
This triggers "move" over "copy" in classes which have a move
constructor and/or move assignment operator. Think of "move" as
auto_ptr copy, or destructive copy.

The earlier papers talk about something called move_ptr. This is the
same thing as unique_ptr. I just changed names midstream. I've posted
a reference implementation here:

http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr.html

I am currently working on proposed wording for the library half of the
proposal - that which is proposed in N1771. This will include proposed
wording for unique_ptr. There are a few minor changes between the
unique_ptr as presented in N1771 and what I will propose in the Fall
mailing (mainly due to some excellent suggestions by Jonathan Turkanis).

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Peter Dimov
Guest





PostPosted: Sat Aug 13, 2005 11:20 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

vladimir kliatchko wrote:
Quote:
Abstract

Over a period of two years we have accumulated significant experience
in developing and then using a family of C++ components implementing
smart pointers. While similar to the smart pointers described by TR1,
our implementation has some important distinctions. Our experience
shows that these distinctions often resulted in both improved usability
and superior performance. In this paper we describe how our
implementation differs from TR1 and propose a number of corresponding
changes to the C++ standard.

To see the entire proposal, follow this link:
http://home.earthlink.net/~phalpern/cpp/KliatchkoSharedPtr.pdf

The first two parts of your proposal - allocator support and aliasing -
are useful and straightforward extensions that I've considered several
times. I'm still not sure whether the std::allocator interface is the
right way to go, but it seems good enough.

I'd drop the is_allocator trait and leave only

shared_ptr( T * p, D d, A a );

Since replacing the allocator at construction time should be a
relatively rare occurence, it's not that much work to provide an
explicit default deleter.

is_allocator should be proposed independently (if at all); if accepted,
the deleter constructor can then be respecified to do the right thing.

tr1::bind did contain the Throws clause that you mention in its
original incarnation... we'll see what we can do to bring it back; I'm
pretty sure that nobody intended to remove it.

I'd reverse the arguments of the aliasing constructor:

shared_ptr( shared_ptr<Y> const & py, T * pt );

to avoid unintended clashes with the deleter constructor. The Effects
clause should simply be:

Constructs a shared_ptr object that shares ownership with py.

The newly constructed shared_ptr doesn't own pt; it owns whatever py
owns.

The Postcondition should be get() == pt; the rest is redundant.

This aliasing constructor does indeed increase the expressive power of
shared_ptr substantially (you can now implement the casts in user code,
for example). I was - and still am - a bit scared of the opportunities
for misuse, though.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
John Nagle
Guest





PostPosted: Sun Aug 14, 2005 12:41 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

Peter Dimov wrote:

Quote:
vladimir kliatchko wrote:
This aliasing constructor does indeed increase the expressive power of
shared_ptr substantially (you can now implement the casts in user code,
for example). I was - and still am - a bit scared of the opportunities
for misuse, though.

Yes.

The whole point of automatic memory management is to make
it automatic and safe. Creating a semiautomatic memory
management system with complex, obscure semantics is
likely to result in complex, obscure bugs.

Microsoft's "Managed C++" system took that route, and
it wasn't pretty.

John Nagle

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Sun Aug 14, 2005 11:28 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

Howard Hinnant wrote:
Quote:
In article <_3KKe.3550$Gx1.2720 (AT) fe11 (DOT) lga>,
[email]vladimir (AT) kliatchko (DOT) com[/email] (vladimir kliatchko) wrote:

shared_ptr<WorkItem> item(&*i, itemsPtr); // aliasing

I've been looking at other ways this type of aliasing can be achieved
with the existing tr1::shared_ptr spec. Fwiw, here's one possibility:

class link_life_to
{
typedef std::shared_ptr<void> D;
public:
link_life_to(const D& d) : d_(d) {}

template <class T> void operator()(T*) {}
private:
D d_;
};

Now you can say:

shared_ptr<WorkItem> item(&*i, link_life_to(itemsPtr)); // aliasing

I believe this has the same effect as the proposed shared_ptr
constructor. A disadvantage though is that it sets up a second
reference count instead of reusing the original reference count. But
there are a couple of advantages.

The extra memory allocation is a really significant disadvantage in our
experience. This extra allocation degrades performance and makes it
impossible to provide a no-failure guarantee. This disadvantage was (to
a large degree) what motivated us to incorporate aliasing into the
shared pointer directly. Also, we considered aliasing done by shared
pointer directly to be much more elegant solution and one that is
consistent with how casts are implemented.

Quote:

One advantage is that it is easier to search for lifetime dependencies
between otherwise unrelated shared_ptr's (search for "link_life_to").

If this is important, though I am not sure that it is, we could provide
an explicit easily searchable "alias" method.

Quote:

Another advantage is that the approach is extendable to any smart
pointer that takes a custom deleter. For example you could use this
tool to make unique_ptr behave like the proposed managed_ptr in that it
would share ownership with a shared_ptr:

typedef unique_ptr<WorkItem, link_life_to> UPtr;
processor.processWorkItem(Uptr(&*i, link_life_to(itemsPtr)));

The resulting unique_ptr actually has a lower overhead compared to
managed_ptr in my experiments (sizeof == 3 words instead of 4 words).

The extra word used by managed_ptr buys us something that was a very
important design goal for us: the type of managed_ptr is not affected
by the type of the deleter. This makes managed_ptr an effective
interface type (see paper for more about this).

Another minor point is that using the technique above with unique_ptr
seems to contradict the very name of the unique_ptr class (unless of
course I misunderstand what the name is supposed to imply).

Quote:

If link_life_to is templated:

template <class D = std::shared_ptr class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}

template private:
D d_;
};

then it can used to link the lifetimes of other types of smart pointers
(as long as they accept custom deleters). In order to use a unique_ptr
as the lifetime to link with, link_life_to needs to be extended to
handle move-only types:

template <class D = std::shared_ptr class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}
link_life_to(D&& d) : d_(std::move(d)) {}
link_life_to(link_life_to&& x) : d_(std::move(x.d_)) {}
link_life_to& operator=(link_life_to&& x)
{d_ = std::move(x.d_); return *this;}

template private:
D d_;
};

Unless of course move members are auto-generated as Mirek Fidler
proposes, in which case you don't need the explicit move constructor or
move assignment.

Or perhaps with N1717 (extended):

template <class D = std::shared_ptr class link_life_to
{
public:
link_life_to(const D& d) : d_(d) {}
link_life_to(D&& d) : d_(std::move(d)) {}
link_life_to(link_life_to&&) {default}
link_life_to& operator=(link_life_to&&) {default}

template private:
D d_;
};

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Thanks for your comments and, also, thanks the links to the unique_ptr
documents. I am still looking through your proposals.

--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Mon Aug 15, 2005 12:46 am    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

Peter Dimov wrote:
Quote:
vladimir kliatchko wrote:
Abstract

Over a period of two years we have accumulated significant experience
in developing and then using a family of C++ components implementing
smart pointers. While similar to the smart pointers described by TR1,
our implementation has some important distinctions. Our experience
shows that these distinctions often resulted in both improved usability
and superior performance. In this paper we describe how our
implementation differs from TR1 and propose a number of corresponding
changes to the C++ standard.

To see the entire proposal, follow this link:
http://home.earthlink.net/~phalpern/cpp/KliatchkoSharedPtr.pdf

The first two parts of your proposal - allocator support and aliasing -
are useful and straightforward extensions that I've considered several
times. I'm still not sure whether the std::allocator interface is the
right way to go, but it seems good enough.

I'd drop the is_allocator trait and leave only

shared_ptr( T * p, D d, A a );

Since replacing the allocator at construction time should be a
relatively rare occurence, it's not that much work to provide an
explicit default deleter.

is_allocator should be proposed independently (if at all); if accepted,
the deleter constructor can then be respecified to do the right thing.

We definitely have no objection to considering is_allocator
independently. But please note that the is_allocator trait is also
important for the implementation of the create_obj method. Without the
trait we would either always require an allocator to be passed to
create_obj or we would have to split create_obj into
create_obj_with_allocator and create_obj_without_allocator. In fact we
have contemplated not using the trait and doing exactly what you
suggested with the constructor. We decided that using the traits was a
better approach but we definitely do not feel strongly about it.

Quote:

tr1::bind did contain the Throws clause that you mention in its
original incarnation... we'll see what we can do to bring it back; I'm
pretty sure that nobody intended to remove it.

I'd reverse the arguments of the aliasing constructor:

shared_ptr( shared_ptr<Y> const & py, T * pt );

to avoid unintended clashes with the deleter constructor.

We actually had a vacillated between the signature of the aliasing
constructor above and the one in the proposal. On one hand we did want,
as you pointed out, to avoid confusion with the deleter. On the other
hand we did want to keep various constructors of various smart pointers
consistent in that the pointer is the first parameter. It seems that
you just tipped the balance in the direction of the first
consideration.

Quote:
The Effects clause should simply be:

Constructs a shared_ptr object that shares ownership with py.

The newly constructed shared_ptr doesn't own pt; it owns whatever py
owns.

The Postcondition should be get() == pt; the rest is redundant.

This aliasing constructor does indeed increase the expressive power of
shared_ptr substantially (you can now implement the casts in user code,
for example). I was - and still am - a bit scared of the opportunities
for misuse, though.

The features that we proposed naturally create some new opportunities
for misuse but, in our opinion, a big reason for the success of C++ is
that it does not limit users in their ability to access advanced
features for the sake of protecting others from themselves. That does
not mean that we do not want to prevent accidental misuse by those who
do not intend to use these features, but the general usage of shared
pointer should not be negatively affected by the mere presence of these
features. On the other hand, not having these features, in our
experience, would often result in complicated and error-prone work
around code with inferior performance.

Quote:

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Thanks for your comments of the first two of the proposed features. We
are very much interested in your opinion of the other two though.
Please let us know what you think.

--vlad

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
vladimir kliatchko
Guest





PostPosted: Mon Aug 15, 2005 12:48 am    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

John Nagle wrote:
Quote:
Peter Dimov wrote:

vladimir kliatchko wrote:
This aliasing constructor does indeed increase the expressive power of
shared_ptr substantially (you can now implement the casts in user code,
for example). I was - and still am - a bit scared of the opportunities
for misuse, though.

Yes.

The whole point of automatic memory management is to make
it automatic and safe. Creating a semiautomatic memory
management system with complex, obscure semantics is
likely to result in complex, obscure bugs.

Can you pls provide some examples of what kind of misuse you are
concerned with? Whenever a shared_ptr is constructed it is always up to
the user to supply a correct object pointer and an appropriate deleter.
The aliasing constructor follows the same pattern with another smart
pointer playing the role of a deleter. As long as we make aliasing
explicit and avoid accidental misuse (i.e., when the user did not
intend to use aliasing to begin with) I see no reason to not make this
feature available.

Quote:

Microsoft's "Managed C++" system took that route, and
it wasn't pretty.

John Nagle

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Peter Dimov
Guest





PostPosted: Mon Aug 15, 2005 3:49 pm    Post subject: Re: Proposal: Improving TR1 Smart Pointers Reply with quote

vladimir kliatchko wrote:
Quote:
Peter Dimov wrote:

I'd drop the is_allocator trait and leave only

shared_ptr( T * p, D d, A a );

Since replacing the allocator at construction time should be a
relatively rare occurence, it's not that much work to provide an
explicit default deleter.

is_allocator should be proposed independently (if at all); if accepted,
the deleter constructor can then be respecified to do the right thing.

We definitely have no objection to considering is_allocator
independently. But please note that the is_allocator trait is also
important for the implementation of the create_obj method. Without the
trait we would either always require an allocator to be passed to
create_obj or we would have to split create_obj into
create_obj_with_allocator and create_obj_without_allocator.

Separating the two is a better design as it supports constructors that
take an allocator as a first argument.

Quote:
I'd reverse the arguments of the aliasing constructor:

shared_ptr( shared_ptr<Y> const & py, T * pt );

to avoid unintended clashes with the deleter constructor.

We actually had a vacillated between the signature of the aliasing
constructor above and the one in the proposal. On one hand we did want,
as you pointed out, to avoid confusion with the deleter. On the other
hand we did want to keep various constructors of various smart pointers
consistent in that the pointer is the first parameter.

This is a false consistency since the constructors taking a pointer
assume ownership of the pointer. Constructors that take a shared_ptr as
a first argument create a copy.

Quote:
The features that we proposed naturally create some new opportunities
for misuse but, in our opinion, a big reason for the success of C++ is
that it does not limit users in their ability to access advanced
features for the sake of protecting others from themselves. That does
not mean that we do not want to prevent accidental misuse by those who
do not intend to use these features, but the general usage of shared
pointer should not be negatively affected by the mere presence of these
features. On the other hand, not having these features, in our
experience, would often result in complicated and error-prone work
around code with inferior performance.

True. The alternative is to provide safe methods that cover the common
aliasing use cases - casts, pointer to member, pointer to container
element. Not particularly appealing.

Quote:
Thanks for your comments of the first two of the proposed features. We
are very much interested in your opinion of the other two though.
Please let us know what you think.

It's too early for that. :-)

create_obj is interesting, although I tend to prefer the free function
formulation:

template<class T, ...> shared_ptr<T> create_object( ... );

Avoiding one of the malloc locks can be a big win; on the other hand,
under a good lock-free malloc we won't gain much. It should also be
noted that create_object will silently ignore a class-specific operator
new.

I assume that the deleter of such a shared_ptr instance invokes the
destructor of T, but the storage is not deallocated until the last
weak_ptr goes away, right?

When an explicit allocator is specified, how do you obtain the storage?
Rebind to pair< sp_control_block, aligned_storage_for? The array
variant probably rebinds to char?

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards All times are GMT
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.