在C ++中完全自定义的流运算符

时间:2018-10-09 22:58:51

标签: c++ stream operator-overloading

我想在c ++中为我的video stabilization project创建一个完全自定义的流管道。

最终结果应类似于:

videofilelocation >> preprocessing() >> analyze() >> stabilize() >> video_out(outputfilename).flush();

因此,preprocessing应该接受输入字符串并加载视频,提取帧等。之后,它应该返回自定义结构framevector,该结构应传递给analyze,依此类推。上。

不幸的是,关于如何实现完全自定义的流运算符,没有明确的教程/说明。 (仅用于std::ostream等)

这怎么办?

3 个答案:

答案 0 :(得分:1)

出于好奇,我将此视为难题,并得到了以下小例子:

#include <iostream>
#include <string>

// a sample object which is subject of object stream
struct Object {
  // contents
  std::string name;
  int a, b;
  // constructors.
  Object(int a, int b): a(a), b(b) { }
  Object(const std::string &name, int a, int b):
    name(name), a(a), b(b)
  { }
};

// a functor to initialize an object (alternatively)
struct source {
  Object &obj;
  source(Object &obj, int a, int b): obj(obj)
  {
    this->obj.a = a; this->obj.b = b;
  }
  operator Object& () { return obj; }
};

// a clear functor

struct clear {
  clear() = default;
  Object& operator()(Object &in) const
  {
    in.a = in.b = 0;
    return in;
  }
};

// make clear functor applicable to object "stream"

Object& operator>>(Object &in, const clear &opClear)
{
  return opClear(in);
}

// a global instance

static clear reset;

// an add functor

struct add {
  const int da, db;
  add(int da, int db): da(da), db(db) { }
  Object& operator()(Object &in) const
  {
    in.a += da; in.b += db;
    return in;
  }
};

// make add functor applicable to object "stream"

Object& operator>>(Object &in, const add &opAdd)
{
  return opAdd(in);
}

// a display functor

struct echo {
  const std::string label;
  explicit echo(const std::string &label = std::string()):
    label(label)
  { }
  Object& operator()(Object &in) const
  {
    std::cout << label
      << "Object '" << in.name << "' (" << in.a << ", " << in.b << ")\n";
    return in;
  }
};

// make display functor applicable to object "stream"

Object& operator>>(Object &in, const echo &opEcho)
{
  return opEcho(in);
}

// a sink functor (actually not needed)

struct null {
  null() = default;
  void operator()(Object&) const { }
};

// make echo functor applicable to object "stream"

void operator>>(Object &in, const null &opNull) { opNull(in); }

// check it out:
int main()
{
  Object obj("obj1", 12, 34);
  // either use obj as source
  obj
    >> echo("before add(-2, -4): ")
    >> add(-2, -4)
    >> echo("after add(-2, -4): ")
    >> reset
    >> echo("after reset: ")
    >> null();
  // or a source operator
  source(obj, 11, 22)
    >> echo("before add(11, -11): ")
    >> add(11, -11)
    >> echo("after add(11, -11): ");
  return 0;
}

输出:

before add(-2, -4): Object 'obj1' (12, 34)
after add(-2, -4): Object 'obj1' (10, 30)
after reset: Object 'obj1' (0, 0)
before add(11, -11): Object 'obj1' (11, 22)
after add(11, -11): Object 'obj1' (22, 11)

Live Demo on coliru

基本原理受流运算符的启发:

operator>>获得一个Object引用,处理该对象(更改其状态)并返回该引用。这允许通过运算符链传递在表达式的最左参数中创建的对象。

要应用“类似操纵器的功能”,函子类与相应的operator>>结合使用。

要提供无需()就可以使用的“操纵器”,还需要一个全局实例。

答案 1 :(得分:1)

  

不幸的是,关于如何实现完全自定义的流运算符,没有明确的教程/说明。

好。这是一个简短的。

operator>>是二进制运算符。为了使其具有灵活性,您需要将其编写为自由函数重载,这意味着您必须在每个步骤中自定义其含义。

为了获得上述语法,您正在寻找建立一个顶部operator>>的调用链,以使一个的输出是下一个的第一个参数。

因此a >> b >> c的真正含义是:operator>>(operator>>(a, b), c)请注意,a >> b的输出是(a >> b) >> c的第一个输入。

这是一个非常简化的编译链。您会发现我到处都使用了值语义。如果您的处理步骤对象是严格的功能对象,则可以通过const&传递它们。如果它们保留了以前的使用状态,那么&& r值引用将需要重载。

#include<fstream>
#include<string>

// a representation of video data
// note-a value type so it will want to own its actual data through a 
// unique_ptr or similar.
struct video_data
{
};

struct preprocessing
{
    void process(video_data&);
};

struct analyze
{
    void process(video_data&);
};

struct stabilize
{
    void process(video_data&);
};

struct video_out
{
    video_out(std::string const& where);
    void process(video_data&);
};

struct flush
{
    void process(video_out&);
};

// now define the interactions
auto operator>>(std::string const& path, preprocessing proc) -> video_data
{
    video_data dat;
    proc.process(dat);
    return dat;
}

auto operator>>(video_data dat, analyze proc) -> video_data
{
    proc.process(dat);
    return dat;
}

auto operator>>(video_data dat, stabilize proc) -> video_data
{
    proc.process(dat);
    return dat;
}

auto operator>>(video_data dat, video_out proc) -> video_out
{
    proc.process(dat);
    return std::move(proc);
}

auto operator>>(video_out dat, flush proc) -> video_out
{
    proc.process(dat);
    return std::move(dat);
}

// now build a chain
int test(std::string const& videofilelocation, std::string const& outputfilename)
{
    videofilelocation >> preprocessing() >> analyze() >> stabilize() >> video_out(outputfilename) >> flush();
} 

答案 2 :(得分:0)

此帮助吗?

class FileLocation        {};
class PreProcessedData    {};

class PreProcessingAction {
    public:
        PreProcessedData doStuff(FileLocation const& file) const
        {
            return PreProcessedData{};
        }
};

PreProcessingAction preprocessing() {
    return PreProcessingAction{};
}

PreProcessedData operator>>(FileLocation const& file, PreProcessingAction const& action)
{
    return action.doStuff(file);
}

int main()
{
    FileLocation   location;

    location >> preprocessing();
}

说实话,这似乎会使代码变得比帮助更复杂。

做这样的事情会不会更容易:

Data     file            = readData("InputName");
Data     preProcData     = preprocessing(file);
Data     analysedData    = analyze(preProcData);
Data     stabalizedData  = stabilize(analysedData);

Output   output("OutputName");    
output << stabalizedData;