samedi 29 janvier 2022

Unable to display multiple progress bars (from my class) on the terminal using threads, why?

I am trying to implement a feature to print multiple progress bars on the screen using threads. I have a ProgressBar class which produces progress bars (in case you need more info about it you can look here, the mutex locks have been removed from that), you need to know the definition of its update method (again, more info about the meanings of all the variables are in the link above):

  template <typename bar_type>
  void ProgressBar <bar_type>::update_output( bar_type iterating_var, std::string output )
   {
    std::cout << check_condition( [ = ]{ return iterating_var == min_; }, feat( tcs, "hcrs" ), null_str )
              << output
              << getColor()
              << empty_space + message_
              << reset( "color" )
              << std::flush
              << check_condition( [ = ]{ return iterating_var == max_ - 1; }, feat( tcs, "scrs" ), null_str );
   }
 
  template <typename bar_type>
  void ProgressBar <bar_type>::update( bar_type iterating_var )
   {
    iterating_var_ = 100 * ( iterating_var - min_ ) / ( max_ - min_ - one( iterating_var ) );
    width_ = ( iterating_var_ + 1 ) / 4;

    if( styles_map_.at( "indicator" ).find( style_ ) != styles_map_.at( "indicator" ).end() )
     {
      output_ = feat( crs, "left", 100 ) + 
                getColor() +
                std::to_string( static_cast <int> ( round( iterating_var_ ++ ) ) ) +
                reset( "color" ) + 
                getStyle();

      update_output( iterating_var, output_ );
     }

    else if( styles_map_.at( "loader" ).find( style_ ) != styles_map_.at( "loader" ).end() )
     {
      output_ = feat( crs, "left", 100 ) + 
                getBrackets_open() + 
                getColor() + 
                getStyle() * width_ + 
                empty_space * ( 25 - width_ ) + 
                reset( "color" ) + 
                getBrackets_close();  
                     
      update_output( iterating_var, output_ );
     }

    else if ( style_.find( style_p_ ) != std::string::npos && style_.find( style_l_ ) != std::string::npos &&
              type_ == "complete"  )
     {
      output_= feat( crs, "left", 100 ) + 
               getBrackets_open() + 
               getColor() + 
               style_l_ * width_ + 
               empty_space * ( 25 - width_ ) + 
               reset( "color" ) +
               getBrackets_close() + 
               getColor() + 
               empty_space + 
               std::to_string( iterating_var_ ++ ) + 
               reset( "color" ) + 
               style_p_; 

      update_output( iterating_var, output_ );
     }
     
    else
     {
      throw std::runtime_error( "ProgressBar style has not been set!" );
     }
   }

The function feat( crs, "left", 100 ) is used to move cursor left and so on. This ProgressBar is used in main in this way for example:

  ProgressBar <int> percentage_bar;
  percentage_bar.setMin( 5 );
  percentage_bar.setMax ( 46 );
  percentage_bar.setStyle( "indicator", "%" );

  cout << "This is a normal percentage bar: " << endl;
  for ( int i = percentage_bar.getMin(); i < percentage_bar.getMax(); i++ )
   {
    sleep_for( milliseconds( 100 ) );
    percentage_bar.update( i );
    //Do some operations...
   }
  cout << endl << endl;

Then, I am creating a new MultiProgressBar class to manage multiple progress bars with threads and display them at the same time in the terminal.

#ifndef MULTIPROGRESSBAR_H
#define MULTIPROGRESSBAR_h

#include <iostream>
#include <type_traits>
#include <tuple>
#include <functional>
#include <mutex>
#include <atomic>
#include <utility>
//#include <progress_bar.h>

namespace osm
 {
  //====================================================
  //     TYPE TO GENERATE INDICES FOR PARAMETER PACKS
  //====================================================
  template <size_t... Is>
  struct indices {};
  
  template <size_t N, size_t... Is>
  struct gen_indices: gen_indices <N - 1, N - 1, Is...> {};
  
  template <size_t... Is>
  struct gen_indices <0, Is...>: indices<Is...> {};
  
  //====================================================
  //     MAKE_MULTIPROGRESSBAR TEMPLATE CLASS
  //====================================================
  template <class... Indicators>
  class make_MultiProgressBar 
   {
    public:
  
     //====================================================
     //     CONSTRUCTORS
     //====================================================
     template <class... Inds>
     make_MultiProgressBar( Inds&&... bars ): bars_{ std::forward <Inds> ( bars )... } {}
  
     //====================================================
     //     OTHER PUBLIC METHODS
     //====================================================
     static size_t size() { return sizeof...( Indicators ); }
  
     template <class Func, class... Args>
     void for_one( size_t idx, Func&& func, Args&&... args )
      {
       //std::lock_guard<std::mutex> lock{mutex_};
       call_one( idx, gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
      }
  
     template <class Func, class... Args>
     void for_each( Func&& func, Args&&... args ) 
      {
       //std::lock_guard<std::mutex> lock{mutex_};
       call_all( gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
      }
  
    private:
  
     //====================================================
     //     OTHER PRIVATE METHODS
     //====================================================
     template <size_t... Ids, class Func, class... Args>
     void call_one( size_t idx, indices <Ids...>, Func func, Args&&... args )
      {
       std::lock_guard<std::mutex> lock{mutex_};
       [](...) {} 
        (
         (idx == Ids &&
          ( ( void ) std::forward <Func> ( func )( std::get <Ids> ( bars_ ), 
              std::forward <Args> ( args )... ), false ) )...
        );   
      }
  
     template <size_t... Ids, class Func, class... Args>
     void call_all( indices <Ids...>, Func func, Args&&... args )
      {
       //std::lock_guard<std::mutex> lock{mutex_};
       auto dummy = { ( func( std::get <Ids>( bars_ ), args...), 0 )... };
       ( void )dummy;
      } 
  
     //====================================================
     //     PRIVATE ATTRIBUTES
     //====================================================
     std::tuple <Indicators&...> bars_;
     std::mutex mutex_;
     std::atomic <bool> started_{ false };
   };
  
  //====================================================
  //     HELPER FUNCTION FOR DEDUCTION GUIDES
  //====================================================
  template <class... Indicators>
  make_MultiProgressBar <typename std::remove_reference <Indicators>::type...>
  MultiProgressBar( Indicators&&... inds ) 
   {
    return { std::forward <Indicators> ( inds )... };
   }
  
  //====================================================
  //     FUNCTOR
  //====================================================
  template <class T> 
  struct type_identity 
   {
    using type = T;
   };
  
  struct updater 
   { 
    template <template <class> class PB, class bar_type>
    auto operator()( PB <bar_type>& pb, typename type_identity <bar_type>::type v ) const
        -> decltype( pb.update( bar_type{} ) ) 
     {
      return pb.update( v );
     }
   };
 }

#endif

and in a main should work as:

#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <iomanip>
#include "../include/osmanip.h"

using namespace osm;
using namespace std;
using namespace std::this_thread;
using namespace std::chrono;
  ProgressBar<int> prog_int;
  prog_int.setMin( 0 );
  prog_int.setMax ( 100 );
  prog_int.setStyle( "complete", "%", "#" );
  prog_int.setBrackets( "[", "]" );

  ProgressBar<int> prog_int_2;
  prog_int_2.setMin( 5 );
  prog_int_2.setMax ( 25 );
  prog_int_2.setStyle( "complete", "%", "#" );
  prog_int_2.setBrackets( "[", "]" );

  ProgressBar<float> prog_float;
  prog_float.setMin( 0.1f );
  prog_float.setMax ( 12.1f );
  prog_float.setStyle( "complete", "%", "#" );
  prog_float.setBrackets( "[", "]" );

  auto bars = MultiProgressBar( prog_int, prog_int_2, prog_float );

  // Job for the first bar
  auto job1 = [&bars]() {
    for (int i = 0; i <= 100; i++) {
      bars.for_one(0, updater{}, i);
      sleep_for( milliseconds( 100 ) );
    }
    cout << endl;
  };

  // Job for the second bar
  auto job2 = [&bars]() {
    for (int i = 5; i <= 25; i++) {
      bars.for_one(1, updater{}, i);
      sleep_for(std::chrono::milliseconds(200));
    }
    cout << endl;
  };

  // Job for the third bar
  auto job3 = [&bars]() {
    for (float i = 0.1f; i <= 12.1f; i += 0.1f) {
      bars.for_one(2, updater{}, i);
      sleep_for(std::chrono::milliseconds(60));
    }
    cout << endl;
  };

  thread first_job(job1);
  thread second_job(job2);
  thread third_job(job3);

  first_job.join();
  second_job.join();
  third_job.join();

The problem is that when I run the code, progress bars are printed overlapped, like this:

0 [00:53] gianluca@ubuntu:~/osmanip (main)$ ./bin/main.exe 
[##                      ] 9.166668%

Instead I want something like:

0 [00:53] gianluca@ubuntu:~/osmanip (main)$ ./bin/main.exe 
[############            ] 45%
[#########               ] 39%
[##################      ] 72%

Do you know how to solve this?

Aucun commentaire:

Enregistrer un commentaire