paul424
paul424

Reputation: 153

Nice Initialization of thread with pure virtual function

Here I was designing snall Pipe class hierarchy, problem is, I want to make the hierarchy minimal to the final user , so the only thing he has to do is to overwrite the virtual function like this :

template <typename T> class Processable{

protected:

public:
    std::thread  processLoop;
    void myWrapper(Processable<T>* pp ){    pp->processLoopFunction();};
    virtual void processLoopFunction() = 0 ;
    Processable(   ):processLoop(&Processable<T>::myWrapper,this,this){ processLoop.detach();};
    ~Processable(  ){};

};


template <typename T>  class Source ;

template <typename T> class Sink : public virtual Processable<T>{


    // virtual void processChunk(void) = 0;
    // virtual void isChunkReady(void) = 0; 
protected:

public:
    Source<T>* mySource;
    Sink():mySource(nullptr){};


};

template <typename T> class Source : public  virtual Processable<T>{

protected:


public:
    lockfreequeue<T>  myOutputDataBuffer;


    void setSink(Sink<T>&);
    virtual void processLoopFunction() = 0 ;
};

template <typename T> class Pipe : public virtual Source<T>, public  virtual Sink<T>{

public:
    virtual void processLoopFunction() = 0 ;

};


template <typename T> Pipe<T>& operator|(Source<T>& lhs, Pipe<T>& rhs  ){ lhs.setSink(rhs) ;return rhs;};

template <typename T> Sink<T>& operator|(Source<T>& lhs, Sink<T>& rhs  ){ lhs.setSink(rhs) ;return rhs;};


template <typename T> class PipeDouble: public Pipe<T>{

public:
    virtual void processLoopFunction()  ;

};    

template <typename T> class OutputSink : public Sink<T>{
public:

    virtual void processLoopFunction() ;

};


template <typename T> void Source<T>::setSink(Sink<T>& pp){        
    pp.mySource = this ;
};      


template <typename T> class FileSource : public Source<T>{
    std::ifstream myInputFile;
public:
    FileSource(string filename):myInputFile(filename){};
    virtual void processLoopFunction()  ;
};

template <typename T> class NumberSource : public  Source<T>{

public:
    NumberSource(){};
    virtual void processLoopFunction() ;           
};        

template <typename T> void OutputSink<T>::processLoopFunction(){   
    T tmp;
    while(true){
    if(this->mySource!=nullptr){
        this->mySource->myOutputDataBuffer.pop(tmp);
        cout << tmp << endl;
    }
    }

}

template <typename T> void FileSource<T>::processLoopFunction(){

    T ll = 0;    

    while(myInputFile >> ll){
    this->myOutputDataBuffer.push(ll);
    }
}

template <typename T> void NumberSource<T>::processLoopFunction(){

    T ll = 0 ; 

    while(true){
    this->myOutputDataBuffer.push(ll);
    ll++;
    ll++;

    }
}    

template <typename T> void PipeDouble<T>::processLoopFunction(){
    T tmp;

    while(true){
        if(this->mySource!=nullptr){
            this->mySource->myOutputDataBuffer.pop(tmp);
            tmp  = tmp + tmp;
            this->myOutputDataBuffer.push(tmp);
        }
    }    
}

Of course I end up with

tom@oberon:~/CPP/Pipes> ./a.out pure virtual method called terminate called without an active exception Aborted

But not always ( for some derived classes it throws that error , for some others don't ) ; Is there some nice way of doing it , besides initializing std::thread processLoop; in every derived class separatly ?

Upvotes: 0

Views: 141

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275650

Don't use inheritance.

template<typename T, typename Engine>
struct Processable {
  Engine e;
  std::thread myThread;

  Processable( Processable&& ) = default;
  Processable( Processable & ) = delete;
  Processable( Processable const& ) = delete;
  Processable( Processable const&& ) = delete;
  Processable() = delete;
  Processable& operator=(Processable const&) = delete;
  Processable& operator=(Processable &&) = delete;


  template<typename... Args>
  explicit Processable(Args&& args):
    e( std::forward<Args>(args) ),
    myThread( &Engine::processLoopFunction, &e, &e )
  {
    myThread.detach();
  }
};

Now clients create an Engine type with a (non-virtual) processLoopFunction, then you instantiate a Procesable<T, Engine>, which automatically does the post-construct work for it. It also perfect forwards the Engine's constructors.

But really, creating a class to do this seems pointless, when all you want is an invokable object. Why not just take an arbitrary type and invoke () on it instead of a named processLoopFunction method?

Similarly, creating thread objects and detaching them is rarely a good idea, as you just made clean shutdown basically impossible.

Upvotes: 0

Related Questions