2013-04-06 65 views
1

让我们假设原来我有使用CRTP了如下设计:如何将CRTP与可变模板一起使用?

template<class Outputter> class Generator { 
protected: 
    vector<int> v; 
private: 
    void work(ostream& out) { 
     // perform first part of some complex operations on v 
     out << *static_cast<Outputter *>(this); 
     // perform second part of some complex operations on v 
     out << *static_cast<Outputter *>(this); 
     // many more .... 
     // perform some final actions 
    } 
public: 
    Generator(unsigned length): v(length) {} 
    friend ostream& operator<<(ostream& out, Outputter&& generator) { 
     // perform some preparation work 
     work(out); 
     // perform some final actions 
     return out; 
    } 
}; 

class SimpleDumpOutputter : public Generator<SimpleDumpOutputter> { 
private: 
    unsigned count; 
public: 
    SimpleDumpOutputter(unsigned length): Generator(length), count() {} 
    friend ostream& operator<<(ostream& out, SimpleDumpOutputter& outputter) { 
     out << "Step " << ++count << " of calculation: " 
     copy(outputter.v.begin(), outputter.v.end(), ostream_iterator<int>(out, " ")); 
     out << endl; 
     return out; 
    } 
}; 

class FancyOutputter : public Generator<FancyOutputter> { // create a graph using graphviz's dot language to visualise v 
private: 
    // abbreviated 
public: 
    FancyOutputter(unsigned length): Generator(length) {} 
    friend ostream& operator<<(ostream& out, FancyOutputter& outputter) { 
     // write statements to out 
     return out; 
    } 
}; 

// some more different Outputters, for example an Outputter that creates a pretty LaTeX document 

在这种设计中,是在vector<int> v执行复杂的计算和打印结果在每一步一个Generator CRTP类模板/计算的一部分使用其派生类朋友operator<<

这是我想要实现的一个有趣的概念:我想在一次执行中以多种格式输出。具体而言,我认为我可以这样做:

template<class Outputters> class AggregateOutputter : public Generator<AggregateOutputter<Outputters...> > { 
private: 
    static const unsigned outputter_count = sizeof...(Outputters); 
    typedef array<ostream *, outputter_count> DestArr; 
    DestArr destinations; 
public: 
    AggregateOutputter(unsigned v_length, DestArr destinations): IsomerGenerator<AggregateOutputter<Outputters...> >(length), destinations(destinations) {} 
    friend ostream& operator<<(ostream&, AggregateOutputter& outputter); // first argument is dummy, because we would use the ostreams in destinations 
} 

的想法是,用户会用,说,AggregateOutputter<SimpleDumpOutputter, FancyOutputter和构建具有两个ostream秒的array的对象。每当Generator在输出器类上调用operator<<时,AggregateOutputter将遍历destinations中的ostreamOutputters中的类型,并调用沿着*dest_iter << *static_cast<Outputter_Iter>(this);的行的东西。

我不知道这将如何工作。我不确定是否可以以这种方式使用多重继承,是否可以在一个array和一组参数化类型之间“拉链”。有没有人知道这种情况?

+0

在您的原始版本中,您的'operator <<'重载是'friend's,但正在使用'this'。这不会编译 – 2013-04-06 19:27:22

+0

@AndyProwl我很抱歉。我试图简化产品代码,但犯了一个错误。实际上'operator <<'调用私有成员函数'work'来完成实际工作。 – kccqzy 2013-04-06 19:32:32

+0

您可以使用称为索引的东西来压缩数组和类型列表。 – Pubby 2013-04-06 19:36:11

回答

3

我修改了你的原创设计。我认为Generator在调用输出操作符时做了一堆计算,这至少是令人惊讶的。同样为了让你的AggregateOutputter输出忽略< <的ostream参数也是令人惊讶的。此外,Outputter与Generator不具有is-a关系。

我试图分开的担忧,并最终没有使用CRTP,但使用可变模板,但我认为它做你想做的。

http://ideone.com/xQrnW4

#include <vector> 
#include <iostream> 
#include <iterator> 
#include <array> 
using namespace std; 

class Generator { 
protected: 
    vector<int> v; 
public: 
    Generator(unsigned length): v(length) {} 

    template<class Outputter> 
    void do_calculations_with_output(Outputter& out){ 
     // perform first part of some complex operations on v 
     out.output(v); 
     // perform second part of some complex operations on v 
     out.output(v); 
     // perform some final actions 
    } 

}; 

class SimpleDumpOutputter { 
private: 

    ostream* out; 
    unsigned count; 
public: 
    SimpleDumpOutputter(ostream& os): out(&os), count() {} 
    template<class C> 
    void output(const C& c) { 
     *out << "Step " << ++count << " of calculation: "; 
     copy(c.begin(),c.end(), ostream_iterator<int>(*out, " ")); 
     *out << endl; 
    } 
}; 

class FancyOutputter { 
    ostream* out; 
    int count; 
public: 
    FancyOutputter(ostream& os): out(&os),count() {} 
    template<class C> 
    void output(const C& c) { 
     // create a graph using graphviz's dot language to ease visualisation of v 
     *out << "Step " << ++count << " of calculation: "; 
     *out << "Graphviz output\n"; 
    } 
}; 

template<class... Outputters> class AggregateOutputter : private Outputters... { 
private: 
    template<class First, class... Rest> 
    struct output_helper{ 
     template<class C> 
     static void do_output(AggregateOutputter* pthis,const C& c){ 
      static_cast<First*>(pthis)->output(c); 
      output_helper<Rest...>::do_output(pthis,c); 
     } 

    }; 

    template<class First> 
    struct output_helper<First>{ 
     template<class C> 
     static void do_output(AggregateOutputter* pthis,const C& c){ 
      static_cast<First*>(pthis)->output(c); 
     } 

    }; 
public: 
    template<class... Out> 
    AggregateOutputter(Out&... out): Outputters(out)...{} 
    template<class C> 
    void output(const C& c) { 
     output_helper<Outputters...>::do_output(this,c); 
    } 

}; 
int main(){ 

    AggregateOutputter<FancyOutputter,SimpleDumpOutputter> out(cout,cout); 

    Generator g(10); 

    g.do_calculations_with_output(out); 

} 
+0

+1漂亮的可变模板体操 – Walter 2013-04-06 22:46:58

+0

非常感谢很多为您的答案。我认为我的设计看起来有点奇怪,这里是我的理由:在调用operator <<时做一堆计算是一种“懒惰评估”。由于计算时间很长,因此只有在用户指定了输出格式化输出以及格式化输出发送到的'ostream'时,才能完成计算。在你的例子中,当'do_calculations_with_output'被调用时,生成器执行计算,所以它的原理相同,只是我添加了语法糖,并使用'operator <<'而不是函数。 – kccqzy 2013-04-07 04:09:19

+0

因为我使用CRTP而不是将输出传递给生成器是因为输出是相当复杂的,需要调用生成器的'protected'成员函数来完成他们的部分工作。 – kccqzy 2013-04-07 04:10:29

0

好了,这里就是我想出了一个解决方案,由约翰·Bandela的解决方案here的启发后。 (见我的回答意见,所以我不认为他的做法符合我的需要)

template<class... Outputters> class AggregateOutputter : public Generator<AggregateOutputter<Outputters...> > { 
private: 
    typedef array<ostream *, sizeof...(Outputters)> DestArr; 
    DestArr destinations; 
    typedef typename DestArr::iterator DestArrIter; 
    struct OutputterHolder : public Outputters... { 
     OutputterHolder(vector<int>& v): Outputters(v)... {} 
    } outputter_holder; 
    template<class First, class... Rest> struct OutputHelper { 
     static void do_output(OutputterHolder *pthis, DestArrIter dest) { 
      **dest << *static_cast<First *>(pthis); 
      OutputHelper<Rest...>::do_output(pthis, ++dest); 
     } 
    }; 
    template<class First> struct OutputHelper<First> { 
     static void do_output(OutputterHolder *pthis, DestArrIter dest) { 
      **dest << *static_cast<First *>(pthis); 
     } 
    }; 
public: 
    template<typename... OstreamStar> AggregateOutputter(unsigned length, OstreamStar... ostreams): Generator<AggregateOutputter<Outputters...> >(length), destinations{{ostreams...}}, outputter_holder(this->v) { 
     static_assert(sizeof...(OstreamStar) == sizeof...(Outputters), "number of outputters and destinations do not match"); 
    } 
    friend ostream& operator<<(ostream& dummy_out, AggregateOutputter& outputter) { 
     OutputHelper<Outputters...>::do_output(&outputter.outputter_holder, outputter.destinations.begin()); 
     // possibly write some logging info to dummy_out 
     return dummy_out; 
    } 
}; 

// to use this: 
ofstream fout("gv.gv"); 
cout << AggregateOutputter<FancyOutputter, SimpleDumpOutputter>(length, &fout, &cout); 

的想法是,除了在约翰的回答output_helper(我已经改名为OutputHelper),有另一个名为OutputterHolder的辅助struct,它继承了所有的Outputters。我还使用的array来存储输出的目的地,并且修改了do_output也取iterator,以便正确的ostream可以匹配。

重要的是,陪变化,我已经改变了保护成员vector<int> vGenerator为参考,即vector<int>& v,使outputter_holder的数据结构,可向指结构AggregateOutputter代替。这也需要在需要vector<int>&的所有输出中添加另一个构造函数。原始构造函数的长度为v现在将使用new分配内存。

我不确定我想出的这个解决方案是最好还是最优雅的解决方案。

相关问题