如何通过重载| | |来链接和序列化函数.操作员 [英] How to chain and serialize functions by overloading the | operator

查看:63
本文介绍了如何通过重载| | |来链接和序列化函数.操作员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找出如何一般性地重载给定基类对象的 operator |()以序列化或链接类似于 pipes 的函数调用>或 operator<<(())的工作原理...我想通过管道操作符将它们​​链接起来...这样,我可以拥有一系列独立的函数,并在一个函数中调用它们数据对象...换句话说,要在同一个数据类型上执行多种转换,例如在流系统中...

I'm trying to figure out how to generically overload the operator|() for a given base class object to serialize or chain function calls that are similar to how pipes or operator<<() works... I'd like to chain them through the pipe operator... This way I can have a series of standalone functions, and call them on a single data object... In other words, to perform multiple transformations on the same data type, like in a streaming system...

请考虑以下伪代码示例:该代码可能无法编译,我没有方便的编译器,并且对于函数指针或函数对象,我可能使用了错误的语法作为运算符中的参数……这只是为了说明模式和行为我在追.

Consider the following pseudo code sample: this code probably won't compile, I don't have my compiler handy and I may be using the wrong syntax for the function pointers or function objects as a parameter in the operators... This is only to illustrate the pattern and behavior that I'm after.

template<typename T>
typedef T(*Func)(T); // Function Pointer for functors-lambdas-etc... 

template<typename T>
struct pipe_object {
    T operator|(T(*Func)(T) func) {
        return func(T);
    }

    T operator()(T(*Func)(T) func) {
        return this->operator|(t, func);
    }
};

然后我可能想用它们像这样:

Then I might want to use them something like this:

constexpr int add_one_f(int x) {
    return (x+1);
}

constexpr int add_two_f(int x) {
   return (x+2);
}


void foo() {
    pipe_object<int> p1 = {};
    pipe_object<int> p2 = {};

    int result = p1(&add_one) | p2(&add_two); 

    // or something like...

    int result = p1 | p2; // ... etc ...

    // or something like:
    p1 = add_one | add_two | p2; // ... etc ...
}

我只是不知道如何在 |()运算符中传播 intput - output .重载两个版本,以便它可以识别 |(lhs,rhs)以及 |(rhs,lhs)?

I just don't know how to propagate the intput - output in the |() operator... Would I have to overload two versions so that it can recognize |(lhs, rhs) as well as |(rhs, lhs)?

不仅如此,如果我想扩展它,以便我的 functors lambdas 接受多个参数,该怎么办...

More than just that, what if I want to expand this so that my functors or lambdas were to take multiple arguments...

我一直在Google上进行搜索,但只发现了一些资源,但没有任何具体,简单,优雅且至少与C ++ 17功能有关的最新信息……

I've been doing Google searches on this and only found a couple of resources but nothing that is concrete, simple, elegant, and up to date at least with C++17 features...

如果您知道有关此主题的任何良好的原始资料,请告诉我!

If you know of any good source materials on this subject please let me know!

推荐答案

首先,我假设您具有一些类似于以下的基本知识

First I assume you have some basics that look like this

#include <iostream>
struct vec2 {
    double x;
    double y;
};
std::ostream& operator<<(std::ostream& stream, vec2 v2) {return stream<<v2.x<<','<<v2.y;}

//real methods
vec2 translate(vec2 in, double a) {return vec2{in.x+a, in.y+a};} //dummy placeholder implementations
vec2 rotate(vec2 in, double a) {return vec2{in.x+1, in.y-1};}
vec2 scale(vec2 in, double a) {return vec2{in.x*a, in.y*a};}

因此,您想要的是用于操作的代理类,其中使用功能和其他参数"构造代理对象.(我将函数设为模板参数,以防止使用函数指针,并帮助优化器进行内联,使开销几乎为零.)

So what you want is a proxy class for operations, where a proxy object is constructed with the function and the "other parameters". (I made the function a template parameter, which prevents the use of function pointers, and helps the optimizer to inline, making this nearly zero overhead.)

#include <type_traits>
//operation proxy class
template<class rhst, //type of the only parameter
     vec2(*f)(vec2,rhst)> //the function to call
class vec2_op1 {
    std::decay_t<rhst> rhs; //store the parameter until the call
public:
    vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
    vec2 operator()(vec2 lhs) {return f(lhs, std::forward<rhst>(rhs));}
};

//proxy methods
vec2_op1<double,translate> translate(double a) {return {a};}
vec2_op1<double,rotate> rotate(double a) {return {a};}
vec2_op1<double,scale> scale(double a) {return {a};}

然后您只需使其可链接

//lhs is a vec2, rhs is a vec2_operation to use
template<class rhst, vec2(*f)(vec2,rhst)>
vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) {return lhs=op(lhs);}

用法很简单:

int main() {
    vec2 v2{3,5};
    v2 | translate(2.5) | rotate(30) | translate(3) | scale(2);
    std::cout << v2;
}

http://coliru.stacked-crooked.com/a/9b58992b36ff12d3

注意:没有分配,没有指针,没有副本或移动.这应该生成与刚刚执行 v2.translate(2.5);相同的代码.v2.rotate(30);v2.scale(10); 直接.

Note: No allocations, no pointers, no copies or moves. This should generate the same code as if you just did v2.translate(2.5); v2.rotate(30); v2.scale(10); directly.

这篇关于如何通过重载| | |来链接和序列化函数.操作员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆