StargazingTux StargazingTux - 6 months ago 47
C++ Question

Creating a pipe that can morph types within without experimental::any

I am trying to create a pipe implementation that would expose an interface to chain multiple manipulations on some given data while maintaining immutability of the said data. The caveat here is that the manipulations (which are, obviously, just pure functions) should be able to morph the type of the data (e. g. a pipe that is created from an OpenCV mat that should pass through an OCR tool, which would result in a string output). That said, I made this implementation which utilizes std::experimental::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);
}
};


So the API for this pipe-thingy is as follows:

Pipe::from(some_data) | do_something | do_something_else || unpacked


Well, it works. The problem is that it is error-prone, since you have to use explicit
any_cast
any time you want to actually access the data concealed by
any
. That said, any reducer function has to guess what type it will receive, and if that doesn't match the reality, that's a runtime error, i.e. a lot of unnecessary try-catches or something in the code, as well as those additional lines for explicit typecasting.

So I have two questions about this 'pipe thingy'.


  1. Is it possible to make this less error-prone?

  2. Is is possible to achieve the same effect without using
    any
    ? Maybe some template classes, anything would do.


Answer Source

Template can indeed remove the use of any, something like:

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);
}

The usage would be

MakePipe(some_data) | do_something | do_something_else || unpacked;
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download