Reputation: 122840
I want to implement a Reciever/Publisher class via templates. I am not sure whether templates is the best for this. However, what I got so far is this:
#include <vector>
template <typename InType,typename OutType> class Pipe {
public:
void addListener(Pipe<OutType,???> l){
listener.push_back(l);
}
virtual OutType process(InType)=0;
void Fill(InType value){
OutType outValue = process(value);
for (int i=0;i<listener.size();i++){
listener[i]->Fill(outValue);
}
}
private:
vector<Pipe<OutType,???>*> listener;
};
A Pipe should allow listener with different OutTypes. Actually, it does not need to know the OutType of its listeners, as the process() is called by the listeners. However, I have to put an template parameter. Is there something wrong with my approach? Or is there a way to avoid specifying the OutType of the listeners?
Upvotes: 2
Views: 396
Reputation: 41321
You can make Pipe<InType, OutType>
inherit a common base class depending only on InType
:
template <typename InType>
class PipeBase {
public:
virtual ~PipeBase() = default;
virtual void Fill(InType value) = 0;
};
template <typename InType, typename OutType>
class Pipe : public PipeBase<InType> {
public:
void addListener(PipeBase<OutType>* l){
listener.push_back(l);
}
void Fill(InType value) { ... }
private:
std::vector<PipeBase<OutType>*> listener;
};
Upvotes: 3
Reputation: 42574
The source of your confusion is conflating the concept of Listener, something that is notified of an event, with Pipe - something that is notified of an event, performs some processing, and then notifies a set of Listeners of the results of that processing. In this model, a Pipe is-a Listener, but a Listener is not necessarily a Pipe.
I'd suggest that you (1) allow any callable object that can accept OutType
to be a listener, (2) have Pipe
accept input via operator()
so it is a valid listener for InType
. You can then use std::function<void(OutType)>
to erase the type of listeners, and while you're at it, use std::function<OutType(Intype)>
to erase the type of the transform function:
template <typename InType, typename OutType>
class Pipe {
public:
Pipe(std::function<OutType(InType)> transform) :
transform_(std::move(transform)) {}
// For lvalues, return reference to *this for chaining.
Pipe& addListener(std::function<void(OutType)> f) & {
listeners_.push_back(std::move(f));
return *this;
}
// For rvalues, return *this by value for chaining.
Pipe addListener(std::function<void(OutType)> f) && {
listeners_.push_back(std::move(f));
return std::move(*this);
}
void operator() (InType value) {
const auto outValue = transform_(std::move(value));
for (auto& listener : listeners_) {
listener(outValue);
}
}
private:
std::function<OutType(InType)> transform_;
std::vector<std::function<void(OutType)>> listeners_;
};
Upvotes: 1