创建一个可以在没有实验的情况下变形的管道::任何
Creating a pipe that can morph types within without experimental::any
我正在尝试创建一个管道实现,该实现将使接口在某些给定数据上链接多个操作,同时维持上述数据的不变性。这里的警告是,操作(显然是纯函数)应该能够变形数据的类型(例如。字符串输出)。也就是说,我进行了使用STD ::实验:: Any的实现。
class Pipe {
any data;
public:
Pipe (any data) : data(data) {}
any unpack () {
return data;
}
Pipe operator| (function<any(any)> reducer) {
return Pipe(reducer(this->unpack()));
}
Pipe operator|| (any & destination) {
destination = unpack();
return *this;
}
static Pipe from (any data) {
return Pipe(data);
}
};
因此,该管道的API如下:
Pipe::from(some_data) | do_something | do_something_else || unpacked
好吧,它有效。问题在于它容易出错,因为您必须随时随地使用明确的any_cast
实际访问any
隐藏的数据。也就是说,任何还原器函数都必须猜测它将收到的类型,如果那不匹配现实,那是一个运行时错误,即代码中的许多不必要的尝试或其他内容,以及这些其他行用于明确的打字。
所以我有两个问题的"管道"。
- 是否可以使此较小的错误?
- 可以不使用
any
实现相同的效果吗?也许有些模板类,任何事情都会做。
模板确实可以删除any
的使用,例如:
template <typename T>
class Pipe {
T data;
public:
Pipe (T data) : data(data) {}
const T& unpack () const { return data; }
T& unpack () { return data; }
template <typename F>
auto operator| (F reducer) -> decltype(reducer(std::declval<T>()))
{
return Pipe<decltype(reducer(data))>(reducer(data));
}
Pipe operator|| (T& destination) const
{
destination = unpack();
return *this;
}
};
template <typename T>
Pipe<T> MakePipe(T data) {
return Pipe<T>(data);
}
用法将为
MakePipe(some_data) | do_something | do_something_else || unpacked;
,因此管道与存储数据有关。
namespace plumbing {
// tags to identify plumping components:
struct pipe_tag {};
struct src_tag{};
struct sink_tag{};
// this lets us tag function objects. To handle function pointers,
// we need a specialization.
template<class F, class Tag>
struct tagged_f:F,Tag{
using F::F;
tagged_f(F&& f):F(std::move(f)){}
};
template<class Tag, class F>
tagged_f<F,Tag> tag(F f){ return {std::move(f)}; }
// detect various tags:
template<class F> using is_pipe = std::is_base_of<pipe_tag, F>;
template<class F> using is_src = std::is_base_of<src_tag, F>;
template<class F> using is_sink = std::is_base_of<sink_tag, F>;
// type erased versions of the plumbing components
// useful to store the end result of a plumbing job:
template<class T>
using gen_sink = tagged_f<std::function<void(T)>, sink_tag>;
template<class T>
using gen_src = tagged_f<std::function<void(gen_sink<T>)>, src_tag>;
template<class In, class Out>
using gen_pipe = tagged_f<std::function<void(gen_src<In>, gen_sink<Out>)>, pipe_tag>;
// SFINAE helper using the is_X templates above:
template<class T, template<class...>class Test>
using check=std::enable_if_t< Test<T>{}, bool >;
// really simple source, pipe and sink helpers:
template<class T>
auto simple_src( T in ){
return tag<src_tag>([=](auto&& sink){ sink(in); });
}
template<class F>
auto simple_pipe( F f ){
return tag<pipe_tag>( [=](auto&& src, auto&& sink){
src([&](auto&& in){sink(f(in));});
});
}
template<class V>
auto vector_sink(V& v){
return tag<sink_tag>([&](auto&&t){ v.push_back(decltype(t)(t)); });
}
// operator| implementations:
// Src|Pipe is a Src:
template<class Src, class Pipe,
check<Src, is_src> =true,
check<Pipe, is_pipe> =true
>
auto operator|( Src src, Pipe pipe ){
return tag<src_tag>([=](auto&& sink){
pipe( src, sink );
});
}
// Pipe|Sink is a Sink:
template<class Pipe, class Sink,
check<Sink, is_sink> =true,
check<Pipe, is_pipe> =true
>
auto operator|( Pipe pipe, Sink sink ){
return tag<sink_tag>([=](auto&& t){
pipe( simple_src(t), sink );
});
}
// Pipe|Pipe is a Pipe:
template<class PipeA, class PipeB,
check<PipeA, is_pipe> =true,
check<PipeB, is_pipe> =true
>
auto operator|( PipeA a, PipeB b ){
return tag<pipe_tag>([=](auto&& src, auto&& sink){
b( src|a, sink );
});
}
// Src|Sink is a callable object on 0 arguments:
template<class Src, class Sink,
check<Src, is_src> =true,
check<Sink, is_sink> =true
>
auto operator|( Src src, Sink sink ){
return [=]{ src(sink); };
}
}
现场示例。
这使您可以设置一个无状态的管道链,键入它(使用gen_pipe<In,Out>
),具有1块的数据变为零或多个,缓冲和在字符串上工作(如果添加终止符号),等等。
t的水槽是一种消耗t。
的函数t的来源是一种消耗t的水槽的函数。这允许它们产生超过1。
从t到u的管道是一个函数,它既采用t源,又有u的源。
数据不存储在管道中,而是流过它们。
using namespace plumbing;
std::vector<int> v;
// build pipe, don't run it:
auto code = simple_src(7)|simple_pipe([](auto x){return x*2;})|vector_sink(v);
// run pipe:
code(); // v contains {14}
// print:
for (auto x:v)
std::cout << x << "n";
这些管道是未构图的,并在连接它们时检查类型是否匹配。
这样您就可以有这样的水槽:
auto print_sink = tag<sink_tag>( [](auto&& x){ std::cout << x; } );
同样的水槽可以连接到多种不同类型的数据。
auto hello_world_src = tag<src_tag>( [](auto&& sink) {
for (char c : "hello world")
sink(c);
});
然后
(hello_world_src|print_sink)();
打印"hello world"
。同时
(simple_src(3.14)|print_sink)();
打印3.14
。
我们甚至可以幻想:
auto foreach_src = [](auto&& c){
return tag<src_tag>([c=decltype(c)(c)](auto&& sink) {
for( auto&& x:c )
sink(decltype(x)(x));
});
};
这是一个范围并在容器元素上返回源的函数。
auto foreach_pipe = tag<pipe_tag>([foreach_src]( auto&& src, auto&& sink ) {
src( [&](auto&& c) {
foreach_src(c)(sink);
} );
});
这是管道。它需要一个范围的来源和一个元素的水槽,并将它们连接起来。
有更多示例
相关文章:
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 为什么在某些情况下不写入此文件?
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 在没有Xcode的情况下在Mac捆绑包中嵌入框架
- UE4-如何在给定4个屏幕坐标的情况下缩放纹理或材质
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 在C++中如何在没有pow的情况下进行基础计算
- 松弛原子与无同步情况下的记忆连贯性
- 在 Windows 上,是否可以让 dll 在不使用 PATH 环境变量的情况下在另一个文件夹中查找依赖项?
- 我是c ++的新手,你能解释一下在这种情况下的指针吗
- 创建一个可以在没有实验的情况下变形的管道::任何