1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-26 10:19:45 +03:00
occt/src/Message/Message_ProgressScope.hxx
dpasukhi a5a7b3185b Coding - Apply .clang-format formatting #286
Update empty method guards to new style with regex (see PR).
Used clang-format 18.1.8.
New actions to validate code formatting is added.
Update .clang-format with disabling of include sorting.
  It is temporary changes, then include will be sorted.
Apply formatting for /src and /tools folder.
The files with .hxx,.cxx,.lxx,.h,.pxx,.hpp,*.cpp extensions.
2025-01-26 00:43:57 +00:00

618 lines
23 KiB
C++

// Created on: 2002-02-22
// Created by: Andrey BETENEV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _Message_ProgressScope_HeaderFile
#define _Message_ProgressScope_HeaderFile
#include <Standard_Assert.hxx>
#include <Standard_TypeDef.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <Precision.hxx>
#include <TCollection_AsciiString.hxx>
class Message_ProgressRange;
class Message_ProgressIndicator;
//! Message_ProgressScope class provides convenient way to advance progress
//! indicator in context of complex program organized in hierarchical way,
//! where usually it is difficult (or even not possible) to consider process
//! as linear with fixed step.
//!
//! On every level (sub-operation) in hierarchy of operations
//! the local instance of the Message_ProgressScope class is created.
//! It takes a part of the upper-level scope (via Message_ProgressRange) and provides
//! a way to consider this part as independent scale with locally defined range.
//!
//! The position on the local scale may be advanced using the method Next(),
//! which allows iteration-like advancement. This method can take argument to
//! advance by the specified value (with default step equal to 1).
//! This method returns Message_ProgressRange object that takes responsibility
//! of making the specified step, either directly at its destruction or by
//! delegating this task to another sub-scope created from that range object.
//!
//! It is important that sub-scope must have life time less than
//! the life time of its parent scope that provided the range.
//! The usage pattern is to create scope objects as local variables in the
//! functions that do the job, and pass range objects returned by Next() to
//! the functions of the lower level, to allow them creating their own scopes.
//!
//! The scope has a name that can be used in visualization of the progress.
//! It can be null. Note that when C string literal is used as a name, then its
//! value is not copied, just pointer is stored. In other variants (char pointer
//! or a string class) the string is copied, which is additional overhead.
//!
//! The same instance of the progress scope! must not be used concurrently from different threads.
//! For the algorithm running its tasks in parallel threads, a common scope is
//! created before the parallel execution, and the range objects produced by method
//! Next() are used to initialise the data pertinent to each task.
//! Then the progress is advanced within each task using its own range object.
//! See example below.
//!
//! Note that while a range of the scope is specified using Standard_Real
//! (double) parameter, it is expected to be a positive integer value.
//! If the range is not an integer, method Next() shall be called with
//! explicit step argument, and the rounded value returned by method Value()
//! may be not coherent with the step and range.
//!
//! A scope can be created with option "infinite". This is useful when
//! the number of steps is not known by the time of the scope creation.
//! In this case the progress will be advanced logarithmically, approaching
//! the end of the scope at infinite number of steps. The parameter Max
//! for infinite scope indicates number of steps corresponding to mid-range.
//!
//! A progress scope created with empty constructor is not connected to any
//! progress indicator, and passing the range created on it to any algorithm
//! allows it executing safely without actual progress indication.
//!
//! Example of preparation of progress indicator:
//!
//! @code{.cpp}
//! Handle(Message_ProgressIndicator) aProgress = ...; // assume it can be null
//! func (Message_ProgressIndicator::Start (aProgress));
//! @endcode
//!
//! Example of usage in sequential process:
//!
//! @code{.cpp}
//! Message_ProgressScope aWholePS(aRange, "Whole process", 100);
//!
//! // do one step taking 20%
//! func1 (aWholePS.Next (20)); // func1 will take 20% of the whole scope
//! if (aWholePS.UserBreak()) // exit prematurely if the user requested break
//! return;
//!
//! // ... do next step taking 50%
//! func2 (aWholePS.Next (50));
//! if (aWholePS.UserBreak())
//! return;
//! @endcode
//!
//! Example of usage in nested cycle:
//!
//! @code{.cpp}
//! // Outer cycle
//! Message_ProgressScope anOuter (theProgress, "Outer", nbOuter);
//! for (Standard_Integer i = 0; i < nbOuter && anOuter.More(); i++)
//! {
//! // Inner cycle
//! Message_ProgressScope anInner (anOuter.Next(), "Inner", nbInner);
//! for (Standard_Integer j = 0; j < nbInner && anInner.More(); j++)
//! {
//! // Cycle body
//! func (anInner.Next());
//! }
//! }
//! @endcode
//!
//! Example of use in function:
//!
//! @code{.cpp}
//! //! Implementation of iterative algorithm showing its progress
//! func (const Message_ProgressRange& theProgress)
//! {
//! // Create local scope covering the given progress range.
//! // Set this scope to count aNbSteps steps.
//! Message_ProgressScope aScope (theProgress, "", aNbSteps);
//! for (Standard_Integer i = 0; i < aNbSteps && aScope.More(); i++)
//! {
//! // Optional: pass range returned by method Next() to the nested algorithm
//! // to allow it to show its progress too (by creating its own scope object).
//! // In any case the progress will advance to the next step by the end of the func2 call.
//! func2 (aScope.Next());
//! }
//! }
//! @endcode
//!
//! Example of usage in parallel process:
//!
//! @code{.cpp}
//! struct Task
//! {
//! Data& Data;
//! Message_ProgressRange Range;
//!
//! Task (const Data& theData, const Message_ProgressRange& theRange)
//! : Data (theData), Range (theRange) {}
//! };
//! struct Functor
//! {
//! void operator() (Task& theTask) const
//! {
//! // Note: it is essential that this method is executed only once for the same Task object
//! Message_ProgressScope aPS (theTask.Range, NULL, theTask.Data.NbItems);
//! for (Standard_Integer i = 0; i < theTask.Data.NbSteps && aPS.More(); i++)
//! {
//! do_job (theTask.Data.Item[i], aPS.Next());
//! }
//! }
//! };
//! ...
//! {
//! std::vector<Data> aData = ...;
//! std::vector<Task> aTasks;
//!
//! Message_ProgressScope aPS (aRootRange, "Data processing", aData.size());
//! for (Standard_Integer i = 0; i < aData.size(); ++i)
//! aTasks.push_back (Task (aData[i], aPS.Next()));
//!
//! OSD_Parallel::ForEach (aTasks.begin(), aTasks.end(), Functor());
//! }
//! @endcode
//!
//! For lightweight algorithms that do not need advancing the progress
//! within individual tasks the code can be simplified to avoid inner scopes:
//!
//! @code
//! struct Functor
//! {
//! void operator() (Task& theTask) const
//! {
//! if (theTask.Range.More())
//! {
//! do_job (theTask.Data);
//! // advance the progress
//! theTask.Range.Close();
//! }
//! }
//! };
//! @endcode
class Message_ProgressScope
{
public:
class NullString; //!< auxiliary type for passing NULL name to Message_ProgressScope constructor
public: //! @name Preparation methods
//! Creates dummy scope.
//! It can be safely passed to algorithms; no progress indication will be done.
Message_ProgressScope()
: myProgress(0),
myParent(0),
myName(0),
myStart(0.),
myPortion(1.),
myMax(1.),
myValue(0.),
myIsActive(false),
myIsOwnName(false),
myIsInfinite(false)
{
}
//! Creates a new scope taking responsibility of the part of the progress
//! scale described by theRange. The new scope has own range from 0 to
//! theMax, which is mapped to the given range.
//!
//! The topmost scope is created and owned by Message_ProgressIndicator
//! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
//! progress indicator.
//!
//! @param[in][out] theRange range to fill (will be disarmed)
//! @param[in] theName new scope name
//! @param[in] theMax number of steps in scope
//! @param[in] isInfinite infinite flag
Message_ProgressScope(const Message_ProgressRange& theRange,
const TCollection_AsciiString& theName,
Standard_Real theMax,
Standard_Boolean isInfinite = false);
//! Creates a new scope taking responsibility of the part of the progress
//! scale described by theRange. The new scope has own range from 0 to
//! theMax, which is mapped to the given range.
//!
//! The topmost scope is created and owned by Message_ProgressIndicator
//! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
//! progress indicator.
//!
//! @param[in][out] theRange range to fill (will be disarmed)
//! @param[in] theName new scope name constant (will be stored by pointer with no deep
//! copy)
//! @param[in] theMax number of steps in scope
//! @param[in] isInfinite infinite flag
template <size_t N>
Message_ProgressScope(const Message_ProgressRange& theRange,
const char (&theName)[N],
Standard_Real theMax,
Standard_Boolean isInfinite = false);
//! Creates a new scope taking responsibility of the part of the progress
//! scale described by theRange. The new scope has own range from 0 to
//! theMax, which is mapped to the given range.
//!
//! The topmost scope is created and owned by Message_ProgressIndicator
//! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
//! progress indicator.
//!
//! @param[in][out] theRange range to fill (will be disarmed)
//! @param[in] theName empty scope name (only NULL is accepted as argument)
//! @param[in] theMax number of steps in scope
//! @param[in] isInfinite infinite flag
Message_ProgressScope(const Message_ProgressRange& theRange,
const NullString* theName,
Standard_Real theMax,
Standard_Boolean isInfinite = false);
//! Sets the name of the scope.
void SetName(const TCollection_AsciiString& theName)
{
if (myIsOwnName)
{
Standard::Free(myName);
myIsOwnName = false;
}
myName = NULL;
if (!theName.IsEmpty())
{
myIsOwnName = true;
myName = (char*)Standard::AllocateOptimal(Standard_Size(theName.Length()) + Standard_Size(1));
char* aName = (char*)myName;
memcpy(aName, theName.ToCString(), theName.Length());
aName[theName.Length()] = '\0';
}
}
//! Sets the name of the scope; can be null.
//! Note! Just pointer to the given string is copied,
//! so do not pass string from a temporary variable whose
//! lifetime is less than that of this object.
template <size_t N>
void SetName(const char (&theName)[N])
{
if (myIsOwnName)
{
Standard::Free(myName);
myIsOwnName = false;
}
myName = theName;
}
public: //! @name Advance by iterations
//! Returns true if ProgressIndicator signals UserBreak
Standard_Boolean UserBreak() const;
//! Returns false if ProgressIndicator signals UserBreak
Standard_Boolean More() const { return !UserBreak(); }
//! Advances position by specified step and returns the range
//! covering this step
Message_ProgressRange Next(Standard_Real theStep = 1.);
public: //! @name Auxiliary methods to use in ProgressIndicator
//! Force update of presentation of the progress indicator.
//! Should not be called concurrently.
void Show();
//! Returns true if this progress scope is attached to some indicator.
Standard_Boolean IsActive() const { return myIsActive; }
//! Returns the name of the scope (may be null).
//! Scopes with null name (e.g. root scope) should
//! be bypassed when reporting progress to the user.
Standard_CString Name() const { return myName; }
//! Returns parent scope (null for top-level scope)
const Message_ProgressScope* Parent() const { return myParent; }
//! Returns the maximal value of progress in this scope
Standard_Real MaxValue() const { return myMax; }
//! Returns the current value of progress in this scope.
//!
//! The value is computed by mapping current global progress into
//! this scope range; the result is rounded up to integer.
//! Note that if MaxValue() is not an integer, Value() can be
//! greater than MaxValue() due to that rounding.
//!
//! This method should not be called concurrently while the progress
//! is advancing, except from implementation of method Show() in
//! descendant of Message_ProgressIndicator.
Standard_Real Value() const;
//! Returns the infinite flag
Standard_Boolean IsInfinite() const { return myIsInfinite; }
//! Get the portion of the indicator covered by this scope (from 0 to 1)
Standard_Real GetPortion() const { return myPortion; }
public: //! @name Destruction, allocation
//! Destructor - closes the scope and adds its scale to the total progress
~Message_ProgressScope()
{
Close();
if (myIsOwnName)
{
Standard::Free(myName);
myIsOwnName = false;
myName = NULL;
}
}
//! Closes the scope and advances the progress to its end.
//! Closed scope should not be used.
void Close();
DEFINE_STANDARD_ALLOC
private: //! @name Internal methods
//! Creates a top-level scope with default range [0,1] and step 1.
//! Called only by Message_ProgressIndicator constructor.
Message_ProgressScope(Message_ProgressIndicator* theProgress);
//! Convert value from this scope to global scale, but disregarding
//! start position of the scope, in the range [0, myPortion]
Standard_Real localToGlobal(const Standard_Real theVal) const;
private:
//! Copy constructor is prohibited
Message_ProgressScope(const Message_ProgressScope& theOther);
//! Copy assignment is prohibited
Message_ProgressScope& operator=(const Message_ProgressScope& theOther);
private:
Message_ProgressIndicator* myProgress; //!< Pointer to progress indicator instance
const Message_ProgressScope* myParent; //!< Pointer to parent scope
Standard_CString myName; //!< Name of the operation being done in this scope, or null
Standard_Real myStart; //!< Start position on the global scale [0, 1]
// clang-format off
Standard_Real myPortion; //!< The portion of the global scale covered by this scope [0, 1]
// clang-format on
Standard_Real myMax; //!< Maximal value of progress in this scope
Standard_Real myValue; //!< Current position advanced within this scope [0, Max]
Standard_Boolean myIsActive; //!< flag indicating armed/disarmed state
Standard_Boolean myIsOwnName; //!< flag indicating if name was allocated or not
Standard_Boolean myIsInfinite; //!< Option to advance by hyperbolic law
private:
friend class Message_ProgressIndicator;
friend class Message_ProgressRange;
};
#include <Message_ProgressRange.hxx>
//=======================================================================
// function : Message_ProgressScope
// purpose :
//=======================================================================
inline Message_ProgressScope::Message_ProgressScope(Message_ProgressIndicator* theProgress)
: myProgress(theProgress),
myParent(0),
myName(0),
myStart(0.),
myPortion(1.),
myMax(1.),
myValue(0.),
myIsActive(theProgress != NULL),
myIsOwnName(false),
myIsInfinite(false)
{
}
//=======================================================================
// function : Message_ProgressScope
// purpose :
//=======================================================================
inline Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange& theRange,
const TCollection_AsciiString& theName,
Standard_Real theMax,
Standard_Boolean isInfinite)
: myProgress(theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent(theRange.myParentScope),
myName(NULL),
myStart(theRange.myStart),
myPortion(theRange.myDelta),
myMax(Max(1.e-6, theMax)), // protection against zero range
myValue(0.),
myIsActive(myProgress != NULL && !theRange.myWasUsed),
myIsOwnName(false),
myIsInfinite(isInfinite)
{
SetName(theName);
Standard_ASSERT_VOID(!theRange.myWasUsed,
"Message_ProgressRange is used to initialize more than one scope");
theRange.myWasUsed = true; // Disarm the range
}
//=======================================================================
// function : Message_ProgressScope
// purpose :
//=======================================================================
template <size_t N>
Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange& theRange,
const char (&theName)[N],
Standard_Real theMax,
Standard_Boolean isInfinite)
: myProgress(theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent(theRange.myParentScope),
myName(theName),
myStart(theRange.myStart),
myPortion(theRange.myDelta),
myMax(Max(1.e-6, theMax)), // protection against zero range
myValue(0.),
myIsActive(myProgress != NULL && !theRange.myWasUsed),
myIsOwnName(false),
myIsInfinite(isInfinite)
{
Standard_ASSERT_VOID(!theRange.myWasUsed,
"Message_ProgressRange is used to initialize more than one scope");
theRange.myWasUsed = true; // Disarm the range
}
//=======================================================================
// function : Message_ProgressScope
// purpose :
//=======================================================================
inline Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange& theRange,
const NullString*,
Standard_Real theMax,
Standard_Boolean isInfinite)
: myProgress(theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent(theRange.myParentScope),
myName(NULL),
myStart(theRange.myStart),
myPortion(theRange.myDelta),
myMax(Max(1.e-6, theMax)), // protection against zero range
myValue(0.),
myIsActive(myProgress != NULL && !theRange.myWasUsed),
myIsOwnName(false),
myIsInfinite(isInfinite)
{
Standard_ASSERT_VOID(!theRange.myWasUsed,
"Message_ProgressRange is used to initialize more than one scope");
theRange.myWasUsed = true; // Disarm the range
}
//=======================================================================
// function : Close
// purpose :
//=======================================================================
inline void Message_ProgressScope::Close()
{
if (!myIsActive)
{
return;
}
// Advance indicator to the end of the scope
Standard_Real aCurr = localToGlobal(myValue);
myValue = (myIsInfinite ? Precision::Infinite() : myMax);
Standard_Real aDelta = myPortion - aCurr;
if (aDelta > 0.)
{
myProgress->Increment(aDelta, *this);
}
Standard_ASSERT_VOID(myParent == 0 || myParent->myIsActive,
"Parent progress scope has been closed before child");
myIsActive = false;
}
//=======================================================================
// function : UserBreak
// purpose :
//=======================================================================
inline Standard_Boolean Message_ProgressScope::UserBreak() const
{
return myProgress && myProgress->UserBreak();
}
//=======================================================================
// function : Next
// purpose :
//=======================================================================
inline Message_ProgressRange Message_ProgressScope::Next(Standard_Real theStep)
{
if (myIsActive && theStep > 0.)
{
Standard_Real aCurr = localToGlobal(myValue);
Standard_Real aNext = localToGlobal(myValue += theStep);
Standard_Real aDelta = aNext - aCurr;
if (aDelta > 0.)
{
return Message_ProgressRange(*this, myStart + aCurr, aDelta);
}
}
return Message_ProgressRange();
}
//=======================================================================
// function : Show
// purpose :
//=======================================================================
inline void Message_ProgressScope::Show()
{
if (myIsActive)
{
myProgress->Show(*this, Standard_True);
}
}
//=======================================================================
// function : localToGlobal
// purpose :
//=======================================================================
inline Standard_Real Message_ProgressScope::localToGlobal(const Standard_Real theVal) const
{
if (theVal <= 0.)
return 0.;
if (!myIsInfinite)
{
if (myMax - theVal < RealSmall())
return myPortion;
return myPortion * theVal / myMax;
}
double x = theVal / myMax;
// return myPortion * ( 1. - std::exp ( -x ) ); // exponent
return myPortion * x / (1. + x); // hyperbola
}
//=======================================================================
// function : Value
// purpose :
//=======================================================================
inline Standard_Real Message_ProgressScope::Value() const
{
if (!myIsActive)
{
return myIsInfinite ? Precision::Infinite() : myMax;
}
// get current progress on the global scale counted
// from the start of this scope
Standard_Real aVal = myProgress->GetPosition() - myStart;
// if progress has not reached yet the start of this scope, return 0
if (aVal <= 0.)
return 0.;
// if at end of the scope (or behind), report the maximum
Standard_Real aDist = myPortion - aVal;
if (aDist <= Precision::Confusion())
return myIsInfinite ? Precision::Infinite() : myMax;
// map the value to the range of this scope [0, Max],
// rounding up to integer, with small correction applied
// to avoid rounding errors
return std::ceil(myMax * aVal / (myIsInfinite ? aDist : myPortion) - Precision::Confusion());
}
#endif // _Message_ProgressScope_HeaderFile