Progress Bar

When running long computations, it is useful to have a visual way of identifying the progress of the code, without relying heavily on vast streams of data. The ProgressBar provides a simple, intuitive way of tracking the status of code, with support for tracking multiple process simultaneously.

Class Definition

template<int Dimension = 1, bool DeleteMode = true, char Symbol = '#', unsigned int MaxHashes = 20>
class JSL::ProgressBar

A neat display widget for tracking the progress of a process by printing a progress bar on the screen, which updates to visually indicate the process is running. Supports arbitrary “levels” of progress bars which can be independently modified through a variadic argument interface.

Template Parameters:
  • Dimension – The number of concurrent bars to print (fixes the number of arguments which must be provided to subsequent functions). Defaults to 1 if 0 template arguments provided

  • DeleteMode – If true, utilises JSL::deleteLine() to remove the previous line before reprinting, such that the bar appears animated. If false, provides a more limited form of animation (false only available in Dimension==1 mode). Defaults to true.

  • Symbol – The symbol which is printed within the bar to fill up space. Defaults to “#”.

  • MaxHashes – The number of symbols in a full bar (dictates the width of the progress bar)

Public Functions

template<typename ...Ts>
inline ProgressBar(Ts... targetCounts)

Constructor function. Accepts a number of integer arguments equal to Dimension, throws an error if this is not true.

Parameters:

targetCounts – The number of processes (i.e. max loop index) that must complete before each bar is considered full.

template<typename ...Ts>
inline void Update(Ts... positions)

Provide a progress update (i.e. loop index) for each of the active bars. Accepts a number of integer arguments equal to Dimension.

Parameters:

positons – The current process count (to be compared against the targets) of the open progress bars

inline void SetName(const std::vector<std::string> &names)

Sets all names for the bars simultaneously.

Parameters:

names – A vector of strings (equal to Dimension), each of which is printed before their respective bar.

inline void SetName(unsigned int idx, const std::string &name)

Changes the name of the idx-th bar.

Parameters:
  • idx – The bar name to be changes

  • name – The string which is printed in the front of the bar

inline void SetName(const std::string &name)

An override for SetName(unsigned int idx, const std::string & name) available only when Dimension == 1.

Parameters:

name – The message to go in front of the bar as it prints

inline void Clear()

Deletes all levels of the bar from the screen, useful once the bar has finished and is no longer needed.

Private Functions

template<typename ...Ts>
inline void UnpackTargets(int target, Ts... targets)

The variadic internal function called by the compiler, looping over the arbitrary number of inputs and storing them in internal vectors.

template<typename ...Ts>
inline void UnrollPositions(const int idx, int pos, Ts... remainder)

The variadic internal function which is called by Update(), loops over the arguments and computes the number of Symbols required by each bar.

inline void ClearScreen()

Loops over the existing bars and erases them using ANSI codes.

inline void PrintPositions_DeleteMode()

The version of Printing which happens if DeleteMode is true &#8212; clears the previous bars using ANSI codes, then overwrites them with the new values.

inline void PrintPositions_RetainMode()

The version of Printing which happens if DeleteMode is false - gradually prints the progress bar one Symbol at a time, then adds the close brace “]” whn the final one is printed.

Private Members

bool firstPrint = true

Toggle to ensure open-brace “[” is printed only once.

std::vector<int> BarTargets

Storage for the target number of processes.

std::vector<double> BarProgress

Current progress value (between 0 and 1)

std::vector<int> Hashes

Current printed number of symbols for each bar.

std::vector<std::string> Names

The names printed in front of each bar.

bool reprintNeeded

Toggle which ensures reprinting only occurs when something changes, prevents unnecessary overhead.

int bufferWidth = 10

Default size allocated for names, increases if a value of anmes would overfill it.

int prevHashes = 0

Used in DeleteMode=false to compute new hashe number.

Example Usage

Simple Case

In the simplest case, we can omit the template arguments, and run the progress bar in the following way:

#include <iostream>
#include "JSL.h"

void timeWastingFunction(int i)
{
        // code that takes some time to complete.....
}

int main(int argc, char * argv[])
{
        int N = 100;
        JSL::ProgressBar pb(N);

        for (int i = 0; i < N; ++i)
        {
                timeWastingFunction(i);
                pb.Update(i);
        }
        return 0;
}

This would output the following bar, which overwrites itself as i advances:

[                           ]  //at i = 0
[##############             ]  //at i = 50
[###########################] // at code end

Multi-Tiered Case

In the case of multiple (potentially nested) processes which must be completed, we can add additional bars:

#include <iostream>
#include "JSL.h"

void tieredTimeWastingFunction(int i, int j)
{
        // code that takes some time to complete.....
}

int main(int argc, char * argv[])
{
        int N = 10;
        int N2 = 1000;
        JSL::ProgressBar<2,False,'@'> pb(N,N2);
        pb.SetNames({"First Loop", "Second Loop"});
        for (int i = 0; i < N; ++i)
        {
                for (int j = 0; j < N2; ++j)
                {
                        tieredTimeWastingFunction(i,j);
                        pb.Update(i,j);
                }
        }
        return 0;
}

At i=j=0 this produces:

First Loop  [                     ]
Second Loop [                     ]

As the inner loop progresses:

First Loop  [                     ]
Second Loop [@@@@@@@@@            ]

When the inner loop completes, the bar resets itself, and the outer loop updates:

As the inner loop progresses:

First Loop  [@@@@                 ]
Second Loop [                     ]

No Delete Case

In the case that, for example, the output of the code is being piped to file, printing ANSI escape codes leads to nonsensical output. In this case, the No-Delete mode is a much safer (if more limited) way to run.

#include <iostream>
#include "JSL.h"

void timeWastingFunction(int i)
{
        // code that takes some time to complete.....
}

int main(int argc, char * argv[])
{
        int N = 100;
        JSL::ProgressBar<1,False> pb(N);

        for (int i = 0; i < N; ++i)
        {
                timeWastingFunction(i);
                pb.Update(i);
        }
        return 0;
}

In this case the output appears very similar to the Simple Case above, but note that the output is printed sequentially, rather than being deleted and then rewritten:

[                             //at i = 0
[##############               //at i = 50
[###########################] // at code end