How to write a template, that would take as argument classes, whose constructors have mutually exclusive signatures?
class A
{
A(){};
public:
int a;
A(int i) : a(i) {};
};
class B{
B(){};
public:
int a,b;
B(int i,int j) : a(i), b(j) {};
};
template <class T> class C {
public:
T* _t;
C(int i[])
{ //???
_t=new T(i[0]); //if T has T(int) signature
_t=new T(i[0],i[1]); //if T has T(int,int) signature
}
~C() {delete _t;}
};
int main()
{
int Ai[]={1,2};
C<A> _c(Ai); // template should work instantiated with A and B
C<B> _c(Ai); //
return 0;
}
The signatures of A and B are fixed (cannot be changed to int[] e.g.). Context: I'm thinking about a wrapper, that would take a (specialized) container type as the template argument, e.g. T=vector<int>, or T=map<int,int>, and the problem arises when constructors need to be called.
Use a variadically-templated constructor:
template <typename T> struct C
{
template <typename ...Args> C(Args &&... args)
: _t(new T(std::forward<Args>(args)...))
{
}
// ... destructor? Rule of five? Don't use pointers!!!
private:
T * _t; // ouch!
};
Usage:
C<A> x(1);
C<B> y(2, 3);
(Real programmers would of course prefer a member std::unique_ptr<T> _t;, with otherwise unchanged semantics, but allowing you to disregard all the comments.)
I believe Kerrek SB's answer is partially right, but incomplete. It fails in that C<T>'s constructor is overly generic. That is, C<T> will construct from anything if you just look at its constructor declaration. You don't find out otherwise until you've selected the constructor and you're instantiating. And by then it is too late.
Concrete example:
Let's say that C<T> has:
friend bool operator<(const C&, const C&);
And now you want to make C<T> the key in a map:
std::map<C<A>, int> m;
// ...
m.erase(m.begin());
This is an error because there are two erase overloads which now look like:
iterator erase(const_iterator position);
size_type erase(const key_type& k);
and m.begin() is an iterator. This iterator will convert with equal ease to both const_iterator and key_type (aka C<A>).
Now this can be fixed by calling:
m.erase(m.cbegin());
instead. But this is just the tip of the iceberg on problems that overly generic constructors cause. For example any code that branches on:
std::is_constructible<C<A>, any type and any number of them>::value
is likely to get false positives because the above will always return true.
The fix is a little messy, but very practical:
template<typename T>
struct C
{
template <class ...Args,
class = typename std::enable_if
<
std::is_constructible<T, Args...>::value
>::type
>
C(Args&& ...args)
: _t(new T(std::forward<Args>(args)...))
{
}
// ...
};
I.e. Add a constraint to the constructor such that it won't be instantiated if it isn't going to work. This is messy, ugly, whatever. Perhaps you want to dress it up with a macro. Fine. But it makes this class work where otherwise it is broken in the examples I mention above (and numerous other problems that tend to trickle in as bug reports one at a time over a period of years).
In addition to Kerrek SB's good advice on using unique_ptr<T> over a raw pointer here, I would like to add:
That this constructor should probably be explicit, at least until it is shown by actual use cases that it really needs to be implicit.
Consider storing a T instead of a (possibly smart) pointer to T. You don't need pointer semantics unless you're actually trying to point to a base class to achieve run-time polymorphism.
In summary: be wary of overly generic code, and downright paranoid of overly generic constructors.
Related
The (EBO/EBCO/Empty Base Optimisation) is widely used in STL, but regardless of the implementation differences, the idea seems to be similar: they put EmptyBase and some member into a packed_pair, std::tuple (in implementations where it supports EBCO), or other boost::compressed_pair-look-alikes.
I've found a few links explaining this technique here and here and they also give this "packing" solution as a good one, and by the look of things the only solution everyone uses.
It all boils down to something like this:
template <class EBase>
class SomeClass {
...
packed_pair<EBase, TElem> pack;
};
and then they'll use some getters to get EBase (Allocator/Deleter/...) and the element.
the pack.first() and pack.second() themselves do not look very attractive, for sure, but otherwise nice technique, very useful, except for one situation...
What if, for example, there's no other member to pack the EmptyBase with? What if those members are inherited from a class that doesn't need the EmptyBase, so you cannot pack it there, so the question is: what's next? Looks like we need to find another way...
In those articles they quickly dumped the inheritance-based solution as a naive one, but what if we elaborate a little on it?
#if (__has_cpp_attribute(no_unique_address) || __cplusplus/100 >= 2020)
// [[no_unique_address]] saves the day, thus an easy case is solved
template <class Base>
class EBO {
public:
[[no_unique_address]] Base elem;
EBO(Base const& b) : elem{b} {}
Base& ebo_unwrap() { return elem; }
};
#else
// Specialising so that we don't inherit from something bad :)
template <class Base, bool Use_inheritance>
class EBO_specialized : public Base {
public:
EBO_specialized(Base const& b) : Base{b} {}
Base& ebo_unwrap() { return *static_cast<Base*>(this); }
};
template <class Base>
class EBO_specialized<Base, false> {
public:
Base elem;
EBO_specialized(Base const& b) : elem{b} {}
Base& ebo_unwrap() { return elem; }
};
template <class Base>
class EBO : public EBO_specialized<Base, Type<Base>( is_empty && !is_final )> {
using EBOBase = EBO_specialized<Base, Type<Base>( is_empty && !is_final )>;
public:
EBO(Base const& d) : EBOBase{d} {}
using EBOBase::ebo_unwrap;
};
#endif
What are the potential problems that can arise when doing EBO this way?
let's imagine some use-case now
template <class Deleter>
class SomePointer : PointerBase, EBO<Deleter> {
...
using PointerBase::raw_ptr;
~SomePointer() { EBO<Deleter>::ebo_unwrap() (*raw_ptr); }
};
(Some) other ways to do Empty Base Class Optimisation:
The easiest one, if you develop for C++20 and beyond or you're using a compiler that provides [[no_unique_address]] as an extension for whatever version of C++ language you're using:
template <class Empty>
class SomeClass {
[[no_unique_address]] Empty empty; // and that's it!
};
The most commonly-used one in pre-C++-20 code, seen in places like STL, the "packing" method, also slightly covered both in the question and in the article in comments.
Pros: It packs an empty base class with another member variable of our class.
Cons: It packs an empty base class with another member variable of out class! So much syntactic noise...
The one proposed in the question:
(if we drop some #ifdefs switching to [[no_unique_address]] when it's supported, here's what we get: )
template <class Base, bool Use_inheritance>
class EBO_specialized : public Base {
public:
EBO_specialized(Base const& b) : Base{b} {}
constexpr Base& get() { return *static_cast<Base*>(this); }
};
template <class Base>
class EBO_specialized<Base, false> {
public:
Base elem;
EBO_specialized(Base const& b) : elem{b} {}
constexpr Base& get() { return elem; }
};
template <class Base>
class EBO : public EBO_specialized<
Base, (std::is_empty_v<Base> && !std::is_final_v<Base>)
> {
using EBOBase = EBO_specialized<
Base, (std::is_empty_v<Base> && !std::is_final_v<Base>)
>;
public:
EBO(Base const& d=Base()) : EBOBase{d} {}
using EBOBase::get;
};
The two articles correctly outline some issues with naive inheritance, i.e. without checking the Base class properties. Those problems are:
Base can be final. It'd be an error inheriting from it.
Base can have virtual functions
inheriting from a template parameter can affect whether a member function is virtual.
Now to our implementation: we explicitly check for emptiness via std::is_empty, which will not be the case for a class containing virtual functions and thus a vptr (in the majority of implementations, in ALL implementations I know of). And the second check for is_final rules out the second possibility.
Bonus: can be used with constexpr, for example when you'd like to use C++20's constexpr new. Same is true for all above-mentioned techniques.
The WTF-way, but admit it, it's nice:)
godbolt link
// No inheritance used!
template <class C, bool IsEmpty=std::is_empty<C>::value>
struct MaybeEmpty { /* C is empty */
constexpr MaybeEmpty(C &) noexcept {/* no-op */};
C& get() { // alas, no constexpr reinterpret_cast
return *reinterpret_cast<C*>(this);
}
};
template <class C>
struct MaybeEmpty<C,false> { /* C is not empty */
C obj;
constexpr MaybeEmpty(C & c) noexcept : obj{c} {}
constexpr C& get() {
return obj;
}
};
Since an empty class has no inner state, we can successfully reinterpret_cast anything into it, so here we reinterpret_cast this, which is a pointer to MaybeEmpty, which is itself empty in this instantiation, so sizeof(MaybeEmpty) == sizeof(Base) == 1(somewhat counterintuitively not 0, due to memory layout rules, but anyway, it's empty).
So, as it uses no inheritance, it doesn't suffer from all of the above-mentioned problems. Except for a new, subtle one: we cannot use constexpr in get().
Edit:
4.1.
template <class C, bool IsEmpty=std::is_empty<C>::value>
struct MaybeEmpty {
constexpr MaybeEmpty(C &) noexcept {};
constexpr C get() {
union {MaybeEmpty self; C base;} cast{*this};
return cast.base;
}
};
template <class C>
struct MaybeEmpty<C,false> {
C obj;
constexpr MaybeEmpty(C & c) noexcept : obj{c} {}
constexpr C get() {
return obj;
}
};
godbolt link
this works with constexpr in clang & msvc but not gcc > 9.4...
P.S. If you've spotted a bug, please write in the comments!
I am implementing something very similar to std::vector but uses array on the stack instead of memory allocation.
The d-tor calls a function that uses SFINAE.
If value_type is POD the function have empty body.
If value_type is normal class such std::string, the function have a body and destroy all the data properly.
Now, I want to be able to use this new std::vector as constexpr. However even the c-tor is declared constexpr, the code does not compiles because the class have non trivial d-tor.
Here is small part of the code:
template<typename T, std::size_t SIZE>
class SmallVector{
constexpr SmallVector() = default;
~SmallVector(){
destructAll_<value_type>();
}
// ...
template<typename X>
typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type
destructAll_() noexcept{
}
};
Is there anything I can do to make class be constexpr if value_type is POD and keeping functionality for non POD data types.
(Not at the same time of course)
until C+20
Unfortunately, there is no way to enable/disable destructor with SFINAE, nor with future concepts. That is because destructos:
can't be templated
can't have arguments
can't have a return type
What you can do is specialize whole class, or better yet, create a base class that contains only the construct/destruct and basic access and specialize that.
template <class T, class Enable = void>
struct X {
~X() {}
};
template <class T>
struct X<T, std::enable_if_t<std::is_pod<T>::value>> {
};
static_assert(std::is_trivially_destructible<X<int>>::value);
static_assert(!std::is_trivially_destructible<X<std::vector<int>>>::value);
C++ 20
As far as I can tell you can constraint a destructor and get exactly what you want in a very simple and elegant solution:
template<typename T, std::size_t SIZE>
class SmallVector{
public:
constexpr SmallVector() = default;
~SmallVector() requires std::is_trivially_destructible_v<T> = default;
~SmallVector()
{
}
};
static_assert(std::is_trivially_destructible_v<SmallVector<int, 4>>);
static_assert(!std::is_trivially_destructible_v<SmallVector<std::string, 4>>);
However this is a brand new feature and there have been some changes lately (e.g. see Disable non-templated methods with concepts) and the compiler support is still sketchy. gcc compiles this just fine, while clang is confused by the fact that there are two definitions of the destructor godbolt
The example of if constexpr in destructor. (C++17 required)
template<typename Tp, typename TLock>
struct LockedPtr {
private:
Tp *m_ptr;
TLock *m_lk;
void prelock(std::mutex *mtx) { mtx->lock(); }
void prelock(std::atomic_flag *atom) { while(atom->test_and_set(std::memory_order_acquire)); }
public:
LockedPtr(Tp *ptr, TLock *mtx)
: m_ptr(ptr), m_lk(mtx) {
prelock(mtx);
}
~LockedPtr() {
if constexpr (std::is_same_v<TLock, std::mutex>)
((std::mutex *)m_lk)->unlock();
if constexpr (std::is_same_v<TLock, std::atomic_flag>)
((std::atomic_flag *)m_lk)->clear(std::memory_order_release);
}
};
These code is the part of RAII locked smart pointer, to adopt to normal std::mutex and spinlock by std::atomic_flag.
Using function overload to match different type in constructor.
Match type by if constexpr and make something unconvertable to pointer in destructor.
So I tried creating a compressed pair using the empty base optimization. I would like it such that if class a and b are empty then compressed_pair<a, b> is empty as well. So I defined my compressed pair something like this:
template <class First, class Second>
struct compressed_pair : First, Second
{
compressed_pair() {}
compressed_pair(const First& x, const Second & y)
: First(x), Second(y)
{}
First& first() { return *this; }
Second& second() { return *this; }
};
However, if one of the types inherit from the other it becomes ambiguous. For example, when I compile this program:
struct a
{};
struct b : a
{};
int main()
{
compressed_pair<a, b> p;
auto x = p.first();
}
I get this error from clang:
compressed_pair.cpp:8:30: error: ambiguous conversion from derived class 'compressed_pair<a, b>' to base class 'a':
struct compressed_pair<struct a, struct b> -> struct a
struct compressed_pair<struct a, struct b> -> struct b -> struct a
First& first() { return *this; }
^~~~~
compressed_pair.cpp:21:16: note: in instantiation of member function 'compressed_pair<a, b>::first' requested here
auto x = p.first();
^
So how can I avoid the ambiguous conversion and still have compressed_pair<a, b> be empty?
The problem that you have is that from compressed_pair there are two bases to which the conversion can apply. You need to be able to drive the compiler into selecting one of them. The first thing that comes to mind is to add another layer of inheritance that you can use as a selector:
template <int N, typename T>
struct element : T {};
template <typename T, typename U>
struct compressed_pair : element<0, T>, element<1, U> { ... };
Then your implementation for the accessors could be:
template <typename T, typename U>
T& compressed_pair<T,U>::first() {
return static_cast<element<0,T>&>(*this);
}
The cast inside the accessor forces the selection of one of the two direct bases, element<0,T>. From that point onwards, there is only one base of type T.
You can also use move the accessor to that intermediate step, rather than have it in the complete type. Finally, through specialization you should be able to provide a different implementation for element that supports non-class types as your current implementation will choke on types like int.
Use virtual inheritance to avoid ambiguity or require that First and Second do not derived from each other. It's an either/or decision. You need to decide whether avoiding ambiguity or allowing the empty base optimisation is more important to you.
Generally speaking, to avoid ambiguity, the one that is inherited more than once (i.e. by both your compressed_pair and the other struct type) needs to be a virtual base. Since the template might be instantiated using anything, this means that both First and Second must be a virtual base of compressed_pair, and a must be a virtual base of b.
If you always know that Second might be derived from First but First will never be derived from Second you can relax this somewhat (with the cost of ambiguity again, if First is derived from Second).
Bear in mind that virtual inheritance does impose some constraints on how the classes work. For example, it affects order of construction of bases, requires any subsequently derived classes to manage construction of virtual bases inherited via base classes, etc.
Following from this question, I want to use an unitialised_allocator with, say, std::vector to avoid default initialisation of elements upon construction (or resize() of the std::vector (see also here for a use case). My current design looks like this:
// based on a design by Jared Hoberock
template<typename T, typename base_allocator >
struct uninitialised_allocator : base_allocator::template rebind<T>::other
{
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_default_constructible<T>::value,
"value type must be default constructible");
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_destructible<T>::value,
"value type must be default destructible");
using base_t = typename base_allocator::template rebind<T>::other;
template<typename U>
struct rebind
{
typedef uninitialised_allocator<U, base_allocator> other;
};
typename base_t::pointer allocate(typename base_t::size_type n)
{
return base_t::allocate(n);
}
// catch default construction
void construct(T*)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)default_
{
base_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};
Then an unitialised_vector<> template could be defined like this:
template<typename T, typename base_allocator = std::allocator<T>>
using uninitialised_vector =
std::vector<T,uninitialised_allocator<T,base_allocator>>;
However, as indicated by my comments, I'm not 100% certain as to what are the appropriate conditions in the static_assert()? (Btw, one may consider SFINAE instead -- any useful comments on this are welcome)
Obviously, one has to avoid the disaster that would ensue from the attempted non-trivial destruction of an uninitialised object. Consider
unitialised_vector< std::vector<int> > x(10); // dangerous.
It was suggested (comment by Evgeny Panasyuk) that I assert trivial constructibility, but this does not seem to catch the above disaster scenario. I just tried to check what clang says about std::is_trivially_default_constructible<std::vector<int>> (or std::is_trivially_destructible<std::vector<int>>) but all I got was a crash of clang 3.2 ...
Another, more advanced, option would be to design an allocator which only elides the default construction for objects for which this would be safe to do so.
Fwiw, I think the design can be simplified, assuming a C++11 conforming container:
template <class T>
class no_init_allocator
{
public:
typedef T value_type;
no_init_allocator() noexcept {}
template <class U>
no_init_allocator(const no_init_allocator<U>&) noexcept {}
T* allocate(std::size_t n)
{return static_cast<T*>(::operator new(n * sizeof(T)));}
void deallocate(T* p, std::size_t) noexcept
{::operator delete(static_cast<void*>(p));}
template <class U>
void construct(U*) noexcept
{
static_assert(std::is_trivially_default_constructible<U>::value,
"This allocator can only be used with trivally default constructible types");
}
template <class U, class A0, class... Args>
void construct(U* up, A0&& a0, Args&&... args) noexcept
{
::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
}
};
I see little advantage to deriving from another allocator.
Now you can let allocator_traits handle rebind.
Template the construct members on U. This helps if you want to use this allocator with some container that needs to allocate something other than a T (e.g. std::list).
Move the static_assert test into the single construct member where it is important.
You can still create a using:
template <class T>
using uninitialised_vector = std::vector<T, no_init_allocator<T>>;
And this still fails to compile:
unitialised_vector< std::vector<int> > x(10);
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think the test for is_trivially_destructible is overkill, unless you also optimize destroy to do nothing. But I see no motivation in doing that since I believe it should get optimized anyway whenever appropriate. Without such a restriction you can:
class A
{
int data_;
public:
A() = default;
A(int d) : data_(d) {}
};
int main()
{
uninitialised_vector<A> v(10);
}
And it just works. But if you make ~A() non trivial:
~A() {std::cout << "~A(" << data_ << ")\n";}
Then, at least on my system, you get an error on construction:
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I.e. A is no longer trivially constructible if it has a non-trivial destructor.
However even with the non-trivial destructor you can still:
uninitialised_vector<A> v;
v.push_back(A());
This works, only because I didn't overreach with requiring a trivial destructor. And when executing this I get ~A() to run as expected:
~A(0)
~A(0)
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.
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.
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).
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:
Add a mechanism to indicate ownership in FooAdder. In FooAdder's destructor, only call the cleanup function if it has ownership.
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.
Create an auxilary type (say, FooAdderRef) that will be used to transfer ownership between FooAdders. It should contain enough information to transfer ownership.
Add a conversion operator (operator FooAdderRef) to FooAdder that relinquishes ownership in FooAdder and returns a FooAdderRef.
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.
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;
}
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.
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.
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...