[cfe-dev] Parser Stmt/Expr Owning Pointer
Howard Hinnant
hhinnant at apple.com
Tue Dec 9 07:32:44 PST 2008
On Dec 9, 2008, at 5:05 AM, Sebastian Redl wrote:
> I'm looking forward to seeing your tricks to allow a namespace scope
> move bind to temporaries but not const lvalues. I don't know how to
> do it, so my move() is a member.
The design is pretty much taken from auto_ptr. It is a painful but
effective means of installing move semantics into a class.
>> And has a disadvantage that if passed to a templated function, the
>> wrong type gets deduced.
>>
> True. I find the likelihood of this in the Clang source low.
Agreed.
>> Fwiw I'm pretty close to a C++03 unique_ptr that is passing all my
>> tests so far. It needs a <type_traits> (which I also have). It
>> doesn't have the "moves from const" bug that my previous emulation
>> attempt had.
>>
> I'd love to see it.
I'll try to get it in soon. I'm not yet positive what "in" means.
Does the clang project want unique_ptr? How should it handle pre-std
library components (what namespace)? Does clang want it prepped for
rvalue-ref support via #ifdef's, or just have the emulation?
>> template <void (Action::*Destroyer)(void*)>
>> class ASTDeleter {
>> Action &Actions;
>>
>> // Reference member prevents copy assignment.
>>
>> public:
>> explicit ASTDeleter(Action &actions) : Actions(actions) {}
>>
>> void operator()(void* Node) {
>> (Actions.*Destroyer)(Node); // no null check needed
>> }
>> };
>>
> Is there a specific reason the constructor can't be implicit? Then
> you could leave out the long typedef here:
>> ExprGuard ex((void*)1, ExprGuard::deleter_type(Actions));
>>
> and just write ExprGuard ex(ptr, Actions).
No reason at all. I was just "winging" an ASTDeleter by imitating
your code without really understanding it at all.
> Also, can I trick your unique_ptr to give up its raw pointer after
> it has been moved from? For convenience in the transition phase,
> that would be invaluable. In my current implementation you can do:
>
> void takesOwnershipButHasNotBeenConvertedToBeSmart(ExprTy*);
>
> ExprOwner ptr(getExprFromSomewhere());
> takesOwnershipButHasNotBeenConvertedToBeSmart(ptr.move());
>
> The important part is that it's always move(), even after converting
> the function to take an ExprOwner.
I'm not positive I understand your question 100%, but I'll try a
shotgun answer. If I don't hit the target, just ask again. :-)
unique_ptr is basically designed to be API compatible with auto_ptr as
much as possible. Like auto_ptr it has members:
T* get() const; // return pointer without giving up ownership
T* release(); // return pointer and give up ownership.
postcondition is get() == 0.
After a move, the source.get() == 0. You can also source.release()
and it will also return 0.
If I'm reading your code example correctly, I think this is as simple
as:
ExprOwner ptr(getExprFromSomewhere());
takesOwnershipButHasNotBeenConvertedToBeSmart(ptr.release());
However, after takesOwnershipButHasNotBeenConvertedToBeSmart is
converted to take a unique_ptr:
void takesOwnership(unique_ptr<T>);
then the client code could change in one of several ways, but would
have to change (else compile-time error):
takesOwnership(unique_ptr<T>(ptr.release()));
or more simply:
takesOwnership(move(ptr));
or if logic allows, just:
takesOwnership(getExprFromSomewhere()); // assumes
getExprFromSomewhere() returns a unique_ptr
So the syntax is different for releasing ownership to a raw pointer,
and transferring ownership to another unique_ptr. And the compiler
will tell you when and where you need to change when
takesOwnershipButHasNotBeenConvertedToBeSmart gets updated. You won't
create run time errors. I believe having different syntax is safer.
release() is by nature more dangerous than move(). If you're using
release(), there is a potential for leaking - you are transferring
ownership to a raw pointer. If you're using move(), there is no
potential for leaking. After the transition period, release() should
be used relatively rarely. It will be helpful to be able to search
for these places and not get drowned out by all the "moves".
If it helps, here is the current C++0X synopsis:
template <class T, class D = default_delete<T>>
class unique_ptr
{
public:
typedef implementation-defined pointer;
typedef T element_type;
typedef D deleter_type;
// constructors
unique_ptr();
explicit unique_ptr(pointer p);
unique_ptr(pointer p, implementation-defined d);
unique_ptr(pointer p, implementation-defined d);
unique_ptr(uniquePtr&& u);
unique_ptr(nullptr_t);
template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);
// destructor
∼unique_ptr();
// assignment
unique_ptr& operator=(unique_ptr&& u);
template <class U, class E> unique_ptr& operator=(unique_ptr<U,
E>&& u);
unique_ptr& operator=(unspecified-pointer-type);
// observers
typename add_lvalue_reference<T>::type operator*() const;
pointer operator->() const;
pointer get() const;
deleter_type& get_deleter();
const deleter_type& get_deleter() const;
explicit operator bool() const;
// modifiers
pointer release();
void reset(pointer p = pointer());
void swap(unique_ptr&& u);
// disable copy from lvalue
unique_ptr(const unique_ptr&) = delete;
template <class U, class E> unique_ptr(const unique_ptr<U, E>&) =
delete;
unique_ptr& operator=(const unique_ptr&) = delete;
template <class U, class E> unique_ptr& operator=(const
unique_ptr<U, E>&) = delete;
};
Ref: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/
n2798.pdf 20.8.12 [unique.ptr].
The emulation I have neglects construction from nullptr_t, but does
support construction and assignment from 0. The member swap is likely
soon to be changed to take unique_ptr& instead of unique_ptr&& (a
minor change). It supports custom pointer types, T == void,
incomplete T except for when the deleter is called, stateful deleters,
and deleters which are lvalue reference types. The ASTDeleter deleter
example I gave is something I would call a stateful deleter. T ==
void is good for the kinds of things we want to do in clang: keep
specific types out of the interface. Use of incomplete (but non-void)
T is also helpful in this department (pimpl pattern).
-Howard
More information about the cfe-dev
mailing list