[cfe-dev] Parser Stmt/Expr Owning Pointer

Howard Hinnant hhinnant at apple.com
Tue Dec 9 12:02:44 PST 2008


On Dec 9, 2008, at 2:39 PM, Sebastian Redl wrote:

> Howard Hinnant wrote:
>> I haven't traced this down exactly, so I'm not sure there's a   
>> problem.  But in:
>>
>>   template <void (Action::*Destroyer)(void*)>
>>   class ASTMove {
>>     ASTOwner<Destroyer> &Moved;
>>
>>   public:
>>     explicit ASTMove(ASTOwner<Destroyer> &moved) : Moved(moved) {}
>>     ...
>>
>> we store a reference to the ASTOwner.  And I also see client code  
>> that  looks like:
>>
>>     ExprOwner Idx(Actions, ParseAssignmentExpression());
>>     if (Idx.isInvalid()) {
>>       SkipUntil(tok::r_square);
>>       return Idx.move();
>>     }
>>
>> Are we in danger of returning a reference to the local variable Idx?
>>
> No. The return type is an ExprResult, which is initialized with the  
> ASTMove. As long as no one gets the idea that using an ASTMove as a  
> return type is a good thing (it *isn't*), there's no problem.

Here's one way you can have the compiler enforce that good rule:

   /// RAII owning pointer for StmtTys and ExprTys. Simple move  
emulation.
   template <void (Action::*Destroyer)(void*)>
   class ASTOwner {

     Action &Actions;
     void *Node;
     bool Invalid;

     void destroy() {
       if (Node)
         (Actions.*Destroyer)(Node);
     }

     // Move emulation
     ASTOwner(ASTOwner&); // DO NOT IMPLEMENT
     ASTOwner& operator=(ASTOwner&); // DO NOT IMPLEMENT

     struct ASTMove
     {
         Action &Actions;
         void *Node;
         bool Invalid;

         ASTMove(Action &actions, void *p, bool Inv)
             : Actions(actions), Node(p), Invalid(Inv) {}
     };

   public:
     operator ASTMove() {return ASTMove(Actions, take(), Invalid);}
     ASTOwner(ASTMove r)
       : Actions(r.Actions), Node(r.Node), Invalid(r.Invalid) {}
     ASTOwner& operator=(ASTMove r) {
       reset(r.ptr_);
       Invalid = r.Invalid;
       // Actions = r.Actions;?
       return *this;
     }
     friend ASTOwner move(ASTOwner& p) {return ASTOwner(ASTMove(p));}
     // End move emulation

     explicit ASTOwner(Action &actions, bool invalid = false)
       : Actions(actions), Node(0), Invalid(invalid) {}
     ASTOwner(Action &actions, void *node)
       : Actions(actions), Node(node), Invalid(false) {}

     void reset(void *node = 0) {
       destroy();
       Node = node;
       Invalid = false;
     }

     void *take() {
       void *Temp = Node;
       Node = 0;
       return Temp;
     }
     void *get() const { return Node; }
     bool isInvalid() const { return Invalid; }
     bool isUsable() const { return !Invalid && Node; }

   };

ASTMove is now a private nested class of ASTOwner.  No one can even  
mention it's existence.  And ASTOwner is moveable but not copyable.   
You can return an ASTOwner from a function (with move(expr)), and pass  
it by value to a sink, as long as the expression is an rvalue or the  
result of move(expr) (which is nothing but an rvalue ASTOwner).

Disclaimer:  I may have some details particular to ASTOwner in the  
above sketch incorrect.  The intent is to show how to add move  
emulation to C++03 types (it's easier when you don't have to deal with  
converting constructors).

-Howard




More information about the cfe-dev mailing list