I don't want to speak for Doug, but here is some working C++0X demo code which I believe demonstrates. Parts of it were borrowed from Sebastian's patch:
#include <memory>
#include <cstdio>
class Action
{
public:
void DeleteStmt(void*) {std::printf("DeleteStmt\n");}
void DeleteExpr(void*) {std::printf("DeleteExpr\n");}
};
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);
}
};
typedef std::unique_ptr<void, ASTDeleter<&Action::DeleteStmt> > StmtGuard;
typedef std::unique_ptr<void, ASTDeleter<&Action::DeleteExpr> > ExprGuard;
ExprGuard
ExprGuardSource(Action& Actions)
{
return ExprGuard((void*)1, ExprGuard::deleter_type(Actions));
}
void
ExprGuardSink(ExprGuard p1)
{
std::printf("ExprGuardSink: begin\n");
{
ExprGuard p2 = std::move(p1);
} // DeleteExpr called here
std::printf("ExprGuardSink: end\n");
}
int main()
{
std::printf("main: begin\n");
void* Body = 0;
Action Actions;
StmtGuard BodyGuard1(Body, StmtGuard::deleter_type(Actions));
ExprGuard BodyGuard2 = ExprGuardSource(Actions);
ExprGuardSink(std::move(BodyGuard2));
std::printf("main: end\n");
}
Output:
main: begin
ExprGuardSink: begin
DeleteExpr
ExprGuardSink: end
main: end
Explanation:
DeleteStmt never fires because the void* in BodyGuard1 is null. The void* briefly owned by BodyGuard2 in main() begins life in ExprGuardSource, gets passed up to main (without a release(), or take() as it is currently spelled), and then passed back down to ExprGuardSink which eventually runs DeleteExpr on it. Both StmtGuard and ExprGuard have member get(), with the same semantics as ASTGuard, and member release() with the same semantics as ASTGuard::take(). They lack the reset() templated on N (since unique_ptr is ignorant of Action), but have the other reset().
Problems:
The above code was compiled with g++-4.3 -std=c++0x -nostdinc++ and the emulation at:
http://home.twcny.rr.com/hinnant/cpp_extensions/unique_ptr_03.html
will not work in the above example (it depended on a gcc bug that has since been fixed). I believe Dave Abrahams has a move emulation package at boost that could possibly be adapted, but that is more work and complexity which may or may not be warranted. I believe it probably works similarly to today's auto_ptr, but perhaps disables moving from lvalues; <shrug> I haven't looked at it in years.
Also one needs to be careful with the Action& lifetime living in the deleter that is getting passed around inside the unique_ptr.
The main difference between unique_ptr<void, ASTDeleter<&Action::DeleteStmt> > and ASTGuard is that the former is moveable and thus both documents and enforces the pointer transfer of ownership from scope to scope. Otherwise they do the same job.
-Howard