views:

134

answers:

7

I would like to write an object generator for a templated RAII class -- basically a function template to construct an object using type deduction of parameters so the types don't have to be specified explicitly.

The problem I foresee is that the helper function that takes care of type deduction for me is going to return the object by value, which will (**) result in a premature call to the RAII destructor when the copy is made. Perhaps C++0x move semantics could help but that's not an option for me.

Anyone seen this problem before and have a good solution?

This is what I have:

template<typename T, typename U, typename V>
class FooAdder
{
private:
  typedef OtherThing<T, U, V> Thing;
  Thing &thing_;
  int a_;
  // many other members
public:
  FooAdder(Thing &thing, int a);
  ~FooAdder();
  FooAdder &foo(T t, U u);
  FooAdder &bar(V v);
};

The gist is that OtherThing has a horrible interface, and FooAdder is supposed to make it easier to use. The intended use is roughly like this:

FooAdder(myThing, 2)
  .foo(3, 4)
  .foo(5, 6)
  .bar(7)
  .foo(8, 9);

The FooAdder constructor initializes some internal data structures. The foo and bar methods populate those data structures. The ~FooAdder dtor wraps things up and calls a method on thing_, taking care of all the nastiness.

That would work fine if FooAdder wasn't a template. But since it is, I would need to put the types in, more like this:

FooAdder<Abc, Def, Ghi>(myThing, 2) ...

That's annoying, because the types can be inferred based on myThing. So I would prefer to create a templated object generator, similar to std::make_pair, that will do the type deduction for me. Something like this:

template<typename T, typename U, typename V>
FooAdder<T, U, V>
AddFoo(OtherThing<T, U, V> &thing, int a)
{
  return FooAdder<T, U, V>(thing, a);
}

That seems problematic: because it returns by value, the stack temporary object will (**) be destructed, which will cause the RAII dtor to run prematurely.

** - if RVO is not implemented. Most compilers do, but it is not required, and can be turned off in gcc using -fno-elide-constructors.

A: 

Since C++03 requires explicitly spelling out the type in every declaration, there's no way to accomplish that without dynamic typing, eg having the template inherit from an abstract base class.

You did get something clever with

AddFoo(myThing, 2) // OK: it's a factory function
  .foo(3, 4)
  .foo(5, 6)
  .bar(7)
  .foo(8, 9); // but object would still get destroyed here

but it will be too much of a pain to code everything in that chain of calls.

C++0x adds auto type deduction, so look into upgrading your compiler, or enabling it if you have it. (-std=c++0x on GCC.)

EDIT: If the above syntax is OK but you want to have several chains in a scope, you could define a swap with void* operation.

 // no way to have a type-safe container without template specification
 // so use a generic opaque pointer
void *unknown_kinda_foo_handle = NULL;
CreateEmptyFoo(myThing, 2) // OK: it's a factory function
  .foo(3, 4)
  .foo(5, 6)
  .bar(7)
  .foo(8, 9)
  .swap( unknown_kinda_foo_handle ) // keep object, forget its type
  ; // destroy empty object (a la move)

// do some stuff

CreateEmptyFoo(myThing, 2) // recover its type (important! unsafe!)
  .swap( unknown_kinda_foo_handle ) // recover its contents
  .bar( 9 ) // do something
  ; // now it's destroyed properly.

This is terribly unsafe, but appears to fit your requirements perfectly.

EDIT: swap with a default-constructed object is also the answer to emulating move in C++03. You need to add a default constructor, and perhaps a resource-free default state wherein the destructor does nothing.

Potatoswatter
You're right that the full type would have to be specified in a declaration (unless using C++0x's `auto`). But I don't need to declare stack variables of type `FooAdder`. I just need a temporary that I can chain calls onto. For my purposes, it won't be too much of a pain to chain everything together.
Dan
@Dan: then you have accomplished that. Instead of messing with copy constructors, consider adding a `swap` method. I'll update my answer with that.
Potatoswatter
The premature dtor call I'm worried about is the one that will happen at the completion of the `AddFoo` call. The one that occurs at the semicolon, after all the `foo` and `bar` calls, is the one that I **want** to happen. I think adding the `swap` method as you did eliminates the wrong dtor call. I agree, that is kinda scary storing it off in a `void *`!
Dan
@Dan: no need to worry about that at all. The temporary object returned by `AddFoo` lasts until the semicolon, no matter where `AddFoo` occurs in the statement or what happens to object itself.
Potatoswatter
Right -- the object returned by `AddFoo` will stick around for the duration of the expression. But isn't there an object created inside the `AddFoo` method that would get copied and then destroyed? Suppose `AddFoo` was written like this instead: `{ FooAdder<T, U, V> temp(thing, a); return temp; }` -- then `temp` would get destroyed at the end of the method. I think the same thing happens with `AddFoo` written as in the question. There's a temporary even though you don't really "see" it explicitly declared.
Dan
@Dan: That definition of `FooAdder` is different. For such cases where copy elision is so obvious, if the compiler complains, I'd add a dummy copy constructor which simply asserts (to verify that the copy constructor is never in fact called) rather than worry too much about implementing it correctly. I'm really not sure that `return FooAdder<…>;` is at all allowed to destroy a temporary before the function returns. Does your compiler really complain about exactly that?
Potatoswatter
Um, no, it does not complain... maybe I don't really have a problem here. A copy ctor that asserts is an interesting idea -- that would find the problem at run time. Better to find out at compile time. A copy ctor declared private and not implemented will not compile, but a copy ctor declared public and not implemented will compile and run (this surprises me).
Dan
As far as whether `return FooAdder<…>;` is allowed to destroy a temporary -- Yes. I don't think the C++ standard requires compilers to implement RVO (although most do) and NRVO (some do). If a compiler did not implement RVO, there would be a copy ctor call and a dtor call. You can test this in GCC by using the option `-fno-elide-constructors`
Dan
@Dan: Ah, thanks, I'll try to remember that… declare it private. Beware things that run without implementation, the compiler is probably weak-linking in the implicit copy constructor. The standard only specifies that the `return` expression is implicitly converted to the return type; does that necessarily mean copy construction? (Do you have a pointer into the standard?) Or could a compiler optimize this without elision? Either way, this is different from RVO, which elides the copy from the result to some destination. It may be that "implicit conversion is faster," the nutjobs were right! ;v)
Potatoswatter
@Dan: oh, in my fascination I overlooked your problem. If you want to implement `move` type semantics, your best bet is to have the copy constructor default-construct and then call `swap`. Such behavior is closely related to `move`.
Potatoswatter
I don't think any conversion is happening here. I think this is considered a case of RVO. Regardless, it's a case where the compiler is permitted (but not required) to elide a call to the copy ctor. See 12.8/15 in the spec.
Dan
Dan
The `swap` trick is good for 'move' semantics if you have a pimpl but in this case I am trying to avoid any dynamic allocations.
Dan
@Dan: my `swap(void*)` proposal requires dynamic allocation but `swap` in the copy constructor is just a clean way to eliminate or better-contain the `dismiss_` bit in Dan's proposal, and get functionality to boot.
Potatoswatter
I'm not sure what you're proposing to swap then. `swap` on something like `std::vector` works because the encapsulated data is dynamically allocated, so the `swap` function can just do a pointer swap instead of having to copy the whole vector around a few times. In my case there is no single pointer to be swapped, rather there are a bunch of individual members (`thing_`, `a_`, etc.).
Dan
@Dan: `swap` whatever members are RAII-sensitive, if they support it. Or use your own proposal (sorry I didn't notice you answered your own question). If you're asking how to move `FooAdder`, which has a `Thing`, but `Thing` is not movable, then the problem has recursed. If the only functions allowed to return your types are your factories, seriously consider simply documenting the dependence on RVO.
Potatoswatter
+1  A: 

You'll need a working copy constructor, but optimizing out such copies is explicitly allowed in the standard and should be quite a common optimization for compilers to make.

I'd say there's probably very little need to worry about the move semantics here (it is possible that it won't work anyway - see the auto_ptr_ref hackery that it takes for std::auto_ptr).

UncleBens
A: 

Here's one solution, but I suspect there are better options.

Give FooAdder a copy ctor with something similar to std::auto_ptr's move semantics. To do this without dynamic memory allocation, the copy ctor can set a flag to indicate that the dtor shouldn't do the wrap-up. Like this:

FooAdder(FooAdder &rhs) // Note: rhs is not const
  : thing_(rhs.thing_)
  , a_(rhs.a_)
  , // etc... lots of other members, annoying.
  , dismiss_(false)
{
  rhs.dismiss_ = true;
}

~FooAdder()
{
  if (!dismiss_)
  {
    // do wrap-up here
  }
}

It's probably sufficient to disable the assignment operator by making it private -- shouldn't be any need to call it.

Dan
Johannes Schaub - litb
@litb, but then you can actually modify a true `const FooAdder`, which would be unexpected at the very least!
MSN
@MSN it wouldn't be unexpected, because the modification can't be observed.
Johannes Schaub - litb
@litb, wouldn't that depend on when the const object gets destructed? If you had a global const object with a non trivial destructor, having it suddenly lose ownership would be visible when the program exits.
MSN
@MSN I was talking about the specific usecase of this question. I don't understand how your statements apply to that usecase. How will a private boolean used to prevent execution of a dtor action twice result in "suddenly loosing ownership" of a global object?
Johannes Schaub - litb
@litb, what I mean is that the const object no longer has ownership when its lifetime ends. That may not be expected in all cases and may be an error in many. However, you are correct; it doesn't matter in this case.
MSN
A: 

A friend template? (tested with gcc only)

template <class T, class U, class V> struct OtherThing
{
    void init() { }
    void fini() { }
};

template <class T, class U, class V>
class Adder
{
private:

    typedef OtherThing<T, U, V> Thing;
    Thing& thing_;
    int a_;

    Adder( const Adder& );
    Adder& operator=( const Adder& );

    Adder( Thing& thing, int a ) : thing_( thing ), a_( a ) {}

public:

    ~Adder() { thing_.fini(); }
    Adder& foo( T, U ) { return *this; }
    Adder& bar( V ) { return *this; }

    template <class X, class Y, class Z> friend
        Adder<X,Y,Z> make_adder( OtherThing<X,Y,Z>&, int );
};

template <class T, class U, class V>
Adder<T,U,V> make_adder( OtherThing<T,U,V>& t, int a )
{
    t.init();
    return Adder<T,U,V>( t, a );
}

int main()
{
    OtherThing<int, float, char> ot;
    make_adder( ot, 10 ).foo( 1, 10.f ).bar( 'a'
        ).foo( 10, 1 ).foo( 1, 1 ).bar( '0' );
    return 0;
}
Nikolai N Fetissov
I think there's a subtlety I didn't explain very well. In this code, when `make_adder` returns, the `~Adder` dtor will be called immediately, before any of the `foo` and `bar` calls are made to the **copy** of `Adder` which is returned (and later destroyed). The `friend`-ship only ensures that code can't call the `Adder` constructor on its own... which perhaps is a good idea but doesn't address the problem.
Dan
+1  A: 

If you want to guarantee that what you want to do will work without using move semantics you need to do what auto_ptr does, which is maintain ownership state and provide a conversion operator to a type that transfers ownership between auto_ptrs.

In your case:

  1. Add a mechanism to indicate ownership in FooAdder. In FooAdder's destructor, only call the cleanup function if it has ownership.
  2. Privatize the copy constructor that takes a const FooAdder &; this prevents the compiler from using the copy constructor on rvalues which would violate your single owner invariant.
  3. Create an auxilary type (say, FooAdderRef) that will be used to transfer ownership between FooAdders. It should contain enough information to transfer ownership.
  4. Add a conversion operator (operator FooAdderRef) to FooAdder that relinquishes ownership in FooAdder and returns a FooAdderRef.
  5. Add a constructor that takes a FooAdderRef and claims ownership from it.

This is identical to what auto_ptr does in case you'd want to look at a real implementation. It prevents arbitrary copying from violating your RAII constraints while allowing you to specify how to transfer ownership from factory functions.

This is also why C++0x has move semantics. Because it's a giant PITA otherwise.

MSN
@MSN: thanks for the reply. That does sound like a major PITA. I don't understand why it needs to be so complicated. Can you explain what trouble I will run into if I use my (naieve?) solution proposed here: http://stackoverflow.com/questions/2739905/better-way-to-write-an-object-generator-for-an-raii-template-class/2740309#2740309 ?
Dan
@Dan, the best explanation I found was here: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor. The basic idea is that this trick prevents moving between const objects while allowing for moving from an rvalue to an lvalue. Your solution, without making the copy constructor private, will not maintain a single owner when initializing a `FooAdder` with a `const FooAdder`.
MSN
@Dan, also, marking the ownership state as `mutable` means that a truly `const FooAdder` could be modified. This is much more verbose but a bit safer than the easier method(s).
MSN
A: 

When I consider problems like this, I usually prefer to think of the interface I wish to have first:

OtherThing<T,U,V> scopedThing = FooAdder(myThing).foo(bla).bar(bla);

I would propose a very simple solution:

template <class T, class U, class V>
class OtherThing: boost::noncopyable
{
public:
  OtherThing(); // if you wish

  class Parameters // may be private if FooAdder is friend
  {
  public:
    template<class,class,class> friend class OtherThing;
    Parameters(int,int,int);
    Parameters(const Parameters& rhs);  // proper resource handling
    ~Parameters();                      // proper resource handling

  private:
    Parameters& operator=(const Parameters&); // disabled

    mutable bool dismiss; // Here is the hack
    int p1;
    int p2;
    int p3;
  }; // Parameters

  OtherThing(const Parameters& p);
};

And then:

template <class T, class U, class V>
OtherThing<T,U,V>::Parameters fooAdder(Thing<T,U,V> thing, bla_type, bla_type);

There is no need for conversion operators and the like with which you risk to alter the noncopyable semantics, simply create a temporary struct from which your final class is constructible that will be used to pass all the parameters and alter the semantics of this struct for proper RAII. This way the final class OtherThing does not have screwed semantics and the nastiness (dismiss boolean) is safely tucked in a temporary that should never be exposed anyway.

You still need to make sure for proper exception handling. Notably it means that the temporary struct is responsible for the resource as long as it's not passed to OtherThing.

I know it does not seem to bring much to the table since you're basically going to hack Parameters instead of OtherThing, but I urge you to consider what this would mean:

OtherThing<T,U,V> scopedThing = /**/;
OtherThing<T,U,V>* anotherThing = new OtherThing<T,U,V>(scopedThing);

The second line is valid with your tentative hacks, since scopedThing can be taken by reference as well as const reference, but it does screw things up as much as it does with std::auto_ptr. In the same vein, you can then have std::vector< OtherThing<T,U,V> > and the compiler is never going to complain...

Matthieu M.
+1  A: 

It seems pretty easy. The questioner himself proposed a nice solution, but he can just use a usual copy constructor with a const-reference parameter. Here is what i proposed in comments:

template<typename T, typename U, typename V>
class FooAdder
{
private:
  mutable bool dismiss;
  typedef OtherThing<T, U, V> Thing;
  Thing &thing_;
  int a_;
  // many other members
public:
  FooAdder(Thing &thing, int a);
  FooAdder(FooAdder const&o);
  ~FooAdder();
  FooAdder &foo(T t, U u);
  FooAdder &bar(V v);
};

FooAdder::FooAdder(Thing &thing, int a)
  :thing_(thing), a_(a), dismiss(false)
{ }

FooAdder::FooAdder(FooAdder const& o)
  :dismiss(false), thing_(o.thing_), a_(o.a_) 
{ o.dismiss = true; }

FooAdder::~FooAdder() {
  if(!dismiss) { /* wrap up and call */ }
}

It Just Works.

template<typename T, typename U, typename V>
FooAdder<T, U, V>
AddFoo(OtherThing<T, U, V> &thing, int a)
{
  return FooAdder<T, U, V>(thing, a);
}

int main() {
  AddFoo(myThing, 2)
    .foo(3, 4)
    .foo(5, 6)
    .bar(7)
    .foo(8, 9);
}

No need for complex templates or smart pointers.

Johannes Schaub - litb
This is what I ended up doing, simple enough.
Dan