Reputation: 783
I have a class that does a transformation on a string, like so
class transer{
transer * parent;
protected:
virtual string inner(const string & s) = 0;
public:
string trans(const string & s) {
if (parent)
return parent->trans(inner(s));
else
return inner(s);
}
transer(transer * p) : parent(p) {}
template <class T>
T create() { return T(this); }
template <class T, class A1> // no variadic templates for me
T create(A1 && a1) { return T(this, std::forward(a1)); }
};
So I can create a subclass
class add_count : public transer{
int count;
add_count& operator=(const add_count &);
protected:
virtual string inner(const string & s) {
return std::to_string((long long)count++) + s;
}
public:
add_count(transer * p = 0) : transer(p), count(0) {}
};
And then I can use the transformations:
void use_transformation(transer & t){
t.trans("string1");
t.trans("string2");
}
void use_transformation(transer && t){
use_trasnformation(t);
}
use_transformation(add_count().create<add_count>());
Is there a better design for this? I'd like to avoid using dynamic allocation/shared_ptr if I can, but I'm not sure if the temporaries will stay alive throughout the call. I also want to be able to have each transer
be able to talk to its parent during destruction, so the temporaries also need to be destroyed in the right order. It's also difficult to create a chained transformation and save it for later, since
sometrans t = add_count().create<trans1>().create<trans2>().create<trans3>();
would save pointers to temporaries that no longer exist. Doing something like
trans1 t1;
trans2 t2(&t1);
trans3 t3(&t2);
would be safe, but annoying. Is there a better way to do these kinds of chained operations?
Upvotes: 2
Views: 406
Reputation: 4291
If you don't want dynamic allocation you either pass the data which is operated on to the function that initiates the chain, or you need a root type which holds it for you ( unless you want excessive copying ). Example ( might not compile ):
struct fooRef;
struct foo
{
fooRef create() { return fooRef( m_Val ); }
foo& operator=( const fooRef& a_Other );
std::string m_Val;
}
struct fooRef
{
fooRef( std::string& a_Val ) : m_Val( a_Val ) {}
fooRef create() { return fooRef( m_Val ); }
std::string& m_Val;
}
foo& foo::operator=( const fooRef& a_Other ) { m_Val = a_Other.m_Val; }
foo startChain()
{
return foo();
}
foo expr = startChain().create().create(); // etc
First the string lies on the temporary foo created from startChain(), all the chained operations operates on that source data. The assignment then at last copies the value over to the named var. You can probably almost guarantee RVO on startChain().
Upvotes: 0
Reputation: 153919
Temporaries will be destructed at the end of the full expression, in the reverse order they were constructed. Be careful about the latter, however, since there are no guarantees with regards to the order of evaluation. (Except, of course, that of direct dependencies: if you need one temporary in order to create the next—and if I've understood correctly, that's your case—then you're safe.)
Upvotes: 5