Question

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?

Was it helpful?

Solution

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.)

OTHER TIPS

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().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top