Действительно, это можно сделать, если библиотека или ваш код обеспечивает перегрузки для operator<<
и operator>>
для его работы. Простой пример того, как можно это сделать:
class transformer {
public:
virtual std::iostream& transform(std::iostream&) = 0;
};
class noise : public transformer {
public:
virtual std::iostream& transform(std::iostream&) {
/* extract, change and put into again */
}
};
class echo : public transformer {
public:
virtual std::iostream& transform(std::iostream&) {
/* extract, change and put into again */
}
};
std::iostream& operator>>(std::iostream& io, transformer& ts) {
return ts.transform(io);
}
int main() {
std::stringstream data;
std::ifstream file("sound.wav");
noise n; echo e;
data << file.rdbuf();
data >> n >> e;
/* pipelined data now ready to be played back */
}
Проблема с использованием чистого std::istream
заключается в том, что вы читаете, но тогда у вас не будет способа вернуть преобразованные данные для следующего шага в конвейере. Таким образом, я использую std::iostream
здесь. Этот подход не кажется эффективным, так как каждый оператор >> вызов будет извлекать все данные и помещать их снова.
Чтобы иметь более эффективный способ потоковой передачи, это должно было бы создать expression template
. Это означает, что при вызове operator>>
преобразование еще не выполнено, но вы возвращаете типы выражений, которые будут записывать цепочку операций в своем типе:
typedef transform< echo< noise< istream > > > pipeline;
std::ifstream file("file.wav");
pipeline pipe(file);
int byte = pipe.get();
будет примером такого типа. Структура трубопроводов декодируется в сам тип. Поэтому в конвейере больше не требуется никаких виртуальных функций. Он не построен по требованию, но использует typedef здесь, чтобы показать принцип. Программирование такой системы не просто. Поэтому вам, вероятно, следует изучить существующие системы, такие как Boost.Iostreams (см. Ниже). Чтобы дать вам представление о том, как это будет выглядеть, вот пример, который я только что написал для вас :):
#include <iostream>
template<typename T>
struct transformer {
int get() {
return static_cast<T*>(this)->read();
}
};
struct echot {
template<typename Chain>
struct chain : transformer< chain<Chain> > {
Chain c;
int read() {
return c.get() + 1;
}
chain(Chain const& c):c(c) { }
};
} echo;
struct noiset {
template<typename Chain>
struct chain : transformer< chain<Chain> > {
Chain c;
int read() {
return c.get() * 2;
}
chain(Chain c):c(c) { }
};
} noise;
template<typename T>
typename T::template chain<std::istream&> operator>>(std::istream& is, T) {
return typename T::template chain<std::istream&>(is);
}
template<typename T, typename U>
typename U::template chain<T> operator>>(T t, U u) {
return typename U::template chain<T>(t);
}
int main() {
std::cout << (std::cin >> echo >> noise).get() << std::endl;
}
Ввод 0 дает здесь код 48 ASCII, который добавляется 1 и умножается на 2, давая значение 98, которое также в конечном итоге выводится. Я думаю, что вы согласны, что это не какой-то код, который автор хотел бы написать. Так что, возможно, посмотрите на повышение.
Boost имеет сложную библиотеку iostreams, которая может делать много вещей. Я уверен, что вы найдете что-то подходящее для этого. Boost.Iostreams