I'm currently toying around with some Computer Vision algorithms and always wanted to learn more about templates, so I came up with the idea to build a templated Image Processing Pipeline.
I want the pipeline to be built of several task blocks which I want to plug into each other sequentially. For a specific block, its input and output types are fixed. I ultimately want to be able to inherently control for a block to only accept the next block if the output type of the current is identical to the input type of the next.
Block_N Block_N+1 _______________ _________________ int | | str | | float --->| str = f1(int) |------>| float = f2(str) |-------> |_______________| |_________________|
Consequently, Block_N+2
would have to accept float
as input and may have an arbitrary output type.
This is what I was able to come up with:
Abstract base classes:
template<class I>
class AbstractInput {
public:
virtual ~AbstractInput() { }
virtual void call( I _input ) = 0;
};
template<class I, class O>
class AbstractPipelineTask : public AbstractInput<I> {
public:
typedef AbstractInput<O>* Ptr;
AbstractPipelineTask( AbstractInput<O>* _nextTask = NULL)
: m_nextTask ( _nextTask ) { }
virtual void call( I _input ) {
m_input = _input;
m_output = executeTask( m_input );
if ( m_nextTask )
m_nextTask->call( m_output );
}
virtual O executeTask( I& _input ) = 0;
protected:
AbstractInput<O>* m_nextTask;
I m_input;
O m_output;
};
Example implementation:
class Str2IntPipelineTask : public AbstractPipelineTask< std::string, int > {
public:
Str2IntPipelineTask( AbstractInput<int>* _nextTask = NULL )
: AbstractPipelineTask( _nextTask ) { }
virtual ~Str2IntPipelineTask() { }
virtual int executeTask( std::string& _input ) {
return atoi( _input.c_str() );
}
};
class Int2IntPipelineTask : public AbstractPipelineTask< int, int> {
public:
Int2IntPipelineTask( AbstractInput<int>* _nextTask = NULL )
: AbstractPipelineTask( _nextTask ) { }
virtual ~Int2IntPipelineTask() { }
virtual int executeTask( int& _input ) {
return ( _input * _input );
}
};
class Int2FloatPipelineTask : public AbstractPipelineTask< int, float > {
public:
Int2FloatPipelineTask( AbstractInput<float>* _nextTask = NULL )
: AbstractPipelineTask( _nextTask ) { }
virtual ~Int2FloatPipelineTask() { }
virtual float executeTask( int& _input ) {
return ( static_cast<float>( _input ) / 42.f );
}
};
Examples:
int _tmain(int argc, _TCHAR* argv[])
{
Int2FloatPipelineTask* pipeTask2 = new Int2FloatPipelineTask( );
Int2IntPipelineTask* pipeTask1 = new Int2IntPipelineTask( pipeTask2 );
Str2IntPipelineTask* pipeTask0 = new Str2IntPipelineTask( pipeTask1 );
pipeTask0->call( "42" );
return 0;
}
What I would really like to know besides some general advice is:
- How can I get rid of the raw pointers
AbstractInput<O>* m_nextTask
? I tried changing thetypedef
ofPtr
tostd::shared_ptr<AbstractInput<O>>
but that broke the derived classes constructors. - Is it possible to get rid of
AbstractInput
?
operator>>
inmain
. \$\endgroup\$