我有一系列函数,这些函数将流作为输入并将转换写入输出流。现在界面看起来像这样:
void phase1(std::istream& in, std::ostream& out);
void phase2(std::istream& in, std::ostream& out);
std::istream data = get_initial_data();
std::stringstream first_result;
phase1(data, first_result);
std::stringstream second_result;
phase2(first_result, second_result);
是否有一种更简单/更自然的方式来链接这些呼叫而无需使用Boost(抱歉)?
答案 0 :(得分:3)
我想你想做:
(phase1 | phase2 | phase3)( in, out );
所有胶水都在您身边。还有,
auto first_part = phase1|phase2;
auto second_part = phase3|phase4;
(first_part | second_part)( in, out );
也应该起作用。
namespace stream {
template<class F=std::function<void(std::istream&, std::ostream&)>>
struct pipe {
F f;
void operator()( std::istream& in, std::ostream& out ) const {
f(in,out);
}
template<class O,
std::enable_if_t< !std::is_same<O, F>{} && std::is_convertible<O, F>{}, bool> = true
>
pipe ( pipe <O> o ):
f(std::move(o.f))
{}
pipe (F fin):
f(std::move(fin))
{}
};
template<class F>
pipe (F)->pipe <F>;
template<class First, class Second>
auto operator|( pipe <First> first, pipe <Second> second )
{
return pipe {[=](auto& in, auto& out){
std::stringstream intermediate;
first( in, intermediate );
second( intermediate, out );
}};
}
}
现在您可以这样做:
std::istream data = get_initial_data();
( pipe {phase1} | pipe {phase2} )( data, out );
我们可以将其扩展到源和接收器,从而将内容粘贴到输入中,但这通常需要连续传递样式来处理生命周期问题。
您还可以使用pipe <>
以类型擦除的方式处理任何流管道对象。
如果您要获取和接收,则如下所示:
namespace stream {
template<class Sig, class F=std::function<Sig>>
struct operation;
template<class R, class...Unused, class F>
struct operation<R(Unused...), F>
{
F f;
static_assert(
std::is_convertible< std::result_of_t< F const&(Unused...) >, R >{}
);
template<class...Args>
R operator()( Args&&...args ) const {
return static_cast<R>(f(std::forward<Args>(args)...));
}
template<class O,
std::enable_if_t< !std::is_same<O, F>{} && std::is_convertible<O, F>{}, bool> = true
>
operation ( operation<R(Unused...), O> o ):
f(std::move(o.f))
{}
operation (F fin):
f(std::move(fin))
{}
};
template<class F=std::function<void(std::istream&, std::ostream&)>>
struct pipe:operation<void(std::istream&, std::ostream&), F> {
using operation<void(std::istream&, std::ostream&), F>::operation;
};
template<class F>
pipe (F)->pipe <F>;
template<class First, class Second>
auto operator|( pipe <First> first, pipe <Second> second )
{
return pipe {[=](auto& in, auto& out){
std::stringstream intermediate;
first( in, intermediate );
second( intermediate, out );
}};
}
template<class F=std::function< void(std::function< void(std::ostream&)>) >>
struct source:operation<void(std::function< void(std::istream&)>), F> {
using operation<void(std::function< void(std::istream&)>), F>::operation;
};
template<class F>
source(F)->source<F>;
template<class F=std::function< void(std::function< void(std::ostream&)>) >>
struct sink:operation<void(std::function< void(std::ostream&)>), F> {
using operation<void(std::function< void(std::ostream&)>), F>::operation;
};
template<class F>
sink(F)->sink<F>;
template<class First, class Second>
auto operator|( source<First> src, pipe<Second> p ) {
return source{[=]( auto&& f ){
src([&](auto&& in){
std::stringstream ss;
p( in, ss );
f( ss );
});
}};
}
template<class First, class Second>
auto operator|( pipe<First> p, sink<Second> snk ) {
return sink{[=]( auto&& f ){
snk([&](auto&& out){
std::stringstream ss;
f(ss);
p(ss, out);
});
}};
}
void copy_f( std::istream& is, std::ostream& os ) {
char c;
while (is.get(c)) {
os << c;
}
}
inline pipe copy{copy_f};
template<class First, class Second>
void operator|( source<First> src, sink<Second> snk ) {
src([&](auto&& in){
snk([&](auto&& out){
copy( in, out );
});
});
}
}
您可以执行以下操作:
using namespace stream;
auto src = source{[](auto&& f){
std::stringstream ss;
ss << "Hello world\n";
f(ss);
}};
auto snk = sink{[](auto&& f){
f(std::cout);
}};
src|copy|copy|copy|snk;
source
是一个功能对象,它依次接受一个功能对象,并将一个istream&
传递给该对象。
sink
是一个功能对象,它依次接受一个功能对象,并将ostream&
传递给该对象。
此双功能语法处理令人讨厌的生命周期问题,并允许您在客户端流用户对流进行填充之前/之后进行清理。
here是一个更疯狂的版本,它支持直接到流/从流的直接管道传输。