Commit 8bc3cc14 authored by Martin Marinov's avatar Martin Marinov Committed by GitHub Enterprise

MTBR-676 Refactor Base to a generic module that is useable outside ReForm (#1)

* Remove boost dependency
* Make the CMakeLists.txt reusable
* Add more configuration options and remove ReForm-specific defaults
* Clean up the File interface and implementation
* Clean up definitions of helper and stream functions
* Use the configured print function for debug output to the console instead of streaming to std::cerr
* Clean up the File flags and HTML support (unused)
* Simplify File and Config to avoid deallocations on app shutdown
* Update (C) notices, clean up code
* Add the macro DEB_if() to simplify the DEB_if_*() macro implementations
parent b19f3112
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.1)
project(Base)
# This is used by ASM for ASM compatibility. Include a file that the parent
# project can pass to set specific compilation flags
if (DEFINED COMMONCOMPILERFLAGFILE)
include(${COMMONCOMPILERFLAGFILE})
endif(DEFINED COMMONCOMPILERFLAGFILE)
my_add_subdir(Code)
my_add_subdir(Config)
my_add_subdir(Debug)
my_add_subdir(Utils)
my_add_subdir(Progress)
my_add_subdir(Security)
my_add_subdir(Test)
set(output_lib "Base")
include_directories(${PROJECT_SOURCE_DIR}/..)
inc_3p_dir(Boost)
add_library(${output_lib} STATIC)
add_library(Base ${SOURCES} ${HEADERS})
add_dependencies(Base ThirdParties)
# target_link_libraries(Base)
function(base_add_subdir subdir)
# initialize these variables since ASM is printing warnings otherwise
set(my_headers "")
set(my_t_impls "")
set(my_sources "")
add_subdirectory(${subdir})
# various ways of organizing these files are possible, use one group
source_group(${subdir} FILES ${subdir}/CMakeLists.txt
${my_headers} ${my_t_impls} ${my_sources})
# exclude template implementation files from the build
set_source_files_properties(${my_t_impls} PROPERTIES HEADER_FILE_ONLY TRUE)
target_sources(${output_lib} PRIVATE ${subdir}/CMakeLists.txt
${my_headers} ${my_t_impls} ${my_sources})
endfunction(base_add_subdir)
base_add_subdir(Code)
base_add_subdir(Config)
base_add_subdir(Debug)
base_add_subdir(Utils)
base_add_subdir(Progress)
base_add_subdir(Security)
base_add_subdir(Test)
target_include_directories(${output_lib} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
#Various useful defaults.
set_target_properties(${output_lib} PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(${output_lib} PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(${output_lib} PROPERTIES C_STANDARD 99)
set_target_properties(${output_lib} PROPERTIES CXX_STANDARD 11)
set_target_properties(${output_lib} PROPERTIES POSITION_INDEPENDENT_CODE ON)
if (MSVC)
add_definitions(-DNOMINMAX)
add_definitions(/W4)
add_definitions(/MP) #build on all cores
add_definitions(/W4) # warnings at level 4
endif (MSVC)
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_CODELINK_HH_INCLUDED
#define BASE_CODELINK_HH_INCLUDED
......
// (C) Copyright 2015 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef QUALITY_HH_INCLUDED
#define QUALITY_HH_INCLUDED
......
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASEDLLEXPORT
#if defined(WIN32) || defined(_WIN32)
......@@ -44,4 +45,7 @@
#define STD_ARRAY_AVAILABLE
#endif
#ifndef _MSC_VER
#define sprintf_s snprintf
#endif
set(my_headers
${CMAKE_CURRENT_SOURCE_DIR}/DebCallStack.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebConfig.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebDefault.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebError.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebFile.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebFileOut.hh
......
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifdef DEB_ON
......@@ -6,10 +6,6 @@
#include "DebOut.hh"
#include <cstring>
#ifndef WIN32
#include <cstdio>
#endif
namespace Debug {
namespace {
......@@ -36,13 +32,13 @@ void append_function(std::string& _str, const char* const _func)
void append_entry(std::string& _str, const Enter* const _entr,
const bool _cmpct, const bool _entr_nmbr, const Enter* const _prev_entr)
{
if (_prev_entr != NULL && // there is a previous entry and it is the same?
if (_prev_entr != nullptr && // there is a previous entry and it is the same?
strcmp(_prev_entr->function(), _entr->function()) == 0)
{// ... so do nothing
return;
}
if (_prev_entr != NULL)
if (_prev_entr != nullptr)
_str.append("/");
if (_cmpct)
......@@ -52,10 +48,8 @@ void append_entry(std::string& _str, const Enter* const _entr,
if (_entr_nmbr)
{
_str.append(".");
std::stringstream ss;
ss << _entr->number();
_str.append(ss.str());
_str += '.';
_str += std::to_string(_entr->number());
}
}
......@@ -73,50 +67,21 @@ const CallStack& CallStack::query()
return modify();
}
// Read a particular call stack element
//bool read(int _up, const char*& _funcname, int& _count)
//{
// const Enter* fcs = call(_up);
// if (fcs != NULL)
// {
// _funcname = fcs->function();
// _count = fcs->number(); // Return most recent deb_enter_count
// return true;
// }
// return false;
//}
//
void CallStack::append(std::string& _str, const bool _entr_nmbr) const
{
const Enter* prev = NULL;
const Enter* prev = nullptr;
for (size_t i = 0, n = depth(); i < n; prev = calls_[i++])
append_entry(_str, calls_[i], true, _entr_nmbr, prev);
}
void CallStack::append_indent(std::string& _str, const int _indt,
const bool _html) const
void CallStack::append_indent(std::string& _str, const int _indt) const
{
if (_indt == 0)
return;
if (_html)
{
char buffer[64];
#ifndef WIN32
sprintf(buffer, "<FONT SIZE=%i>", _indt);
#else
sprintf_s(buffer, sizeof(buffer), "<FONT SIZE=%i>", _indt);
#endif
_str.append(buffer);
}
int num = (int)calls_.size();
int i0 = 0;
if (!_html)
++i0; // Don't waste whitespace on first level indent if .txt
int i0 = 1;
for (int i = i0; i < num; ++i)
_str.append(" ");
if (_html)
_str.append(":&nbsp;</FONT>\n");
}
}//namespace Debug
......
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBCALLSTACK_HH_INCLUDED
#define BASE_DEBCALLSTACK_HH_INCLUDED
......@@ -32,19 +32,14 @@ public:
if (_up < (int)n)
return calls_[n - 1 - _up];
else
return NULL;
return nullptr;
}
/*!
Append the full call stack.
*/
//! Append the full call stack.
void append(std::string& _str, const bool _entr_nmbr = true) const;
/*!
Append and indent to the string
*/
void append_indent(std::string& _str, const int _indt, const bool _html
) const;
//! Append and indent to the string
void append_indent(std::string& _str, const int _indt) const;
private:
std::vector<const Enter*> calls_;
......
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifdef DEB_ON
#include "DebConfig.hh"
#include "DebDefault.hh"
#include "DebFile.hh"
#include "Base/Utils/Environment.hh"
#include <fstream>
#include <sstream>
#include <iostream>
#include <list>
#include <string>
#include <map>
......@@ -49,47 +49,42 @@ public:
}
private:
static bool search(const std::string& _flnm,
const std::list<std::string>& _sel_strings)
typedef std::list<std::string> StringList;
// list of strings to be found inside the file name.
StringList file_selct_strngs_;
// list of strings to be found inside the function name.
StringList func_selct_strngs_;
private:
static bool search(const std::string& _str, const StringList& _slcts)
{
for (std::list<std::string>::const_iterator sel_it = _sel_strings.begin(); sel_it != _sel_strings.end(); ++sel_it)
for (const auto& slct : _slcts)
{
const std::string& sel = *sel_it;
if (_flnm.find(sel) != std::string::npos)
if (_str.find(slct) != std::string::npos)
return true;
}
return false;
}
private:
// list of strings to be found inside the file name.
std::list<std::string> file_selct_strngs_;
// list of strings to be found inside the function name.
std::list<std::string> func_selct_strngs_;
};
}//namespace
class Config::Impl
{
public:
Impl() : dflt_lvl_(Default::LEVEL) { read(); }
void print_char_to_cerr(const char _c) { std::cerr << _c; }
void read();
int level(const char* const _flnm, const char* const _fnct) const;
class Config::LevelFilterMap : public std::map<int, FilterLevelSelector> {};
private:
int dflt_lvl_;
typedef std::map<int, FilterLevelSelector> LevelFilterMap;
LevelFilterMap lvl_fltrs_; // filters for each level
};
void Config::Impl::read()
bool Config::load(const char* const _cnfg_envr, const char* const _cnfg_flnm)
{
const std::string flnm =
System::Environment::variable("REFORM_DEB_CONFIG", "reform_deb.cfg");
const auto flnm = System::Environment::variable(_cnfg_envr, _cnfg_flnm);
std::ifstream cnfg_strm(flnm.c_str());
if (!cnfg_strm.is_open())
return false;
delete lvl_fltrs_;
lvl_fltrs_ = new LevelFilterMap;
std::string line;
while (std::getline(cnfg_strm, line))
{
......@@ -97,7 +92,7 @@ void Config::Impl::read()
std::string type;
line_stream >> type;
void (FilterLevelSelector::*add_string)(const std::string&) = NULL;
void (FilterLevelSelector::*add_string)(const std::string&) = nullptr;
if (type == "all")
{}
......@@ -110,9 +105,9 @@ void Config::Impl::read()
int lvl;
line_stream >> lvl;
if (add_string == NULL)
if (add_string == nullptr)
{
dflt_lvl_ = lvl; // We have read the default level.
output_level = lvl; // We have read the default level.
continue;
}
char colon;
......@@ -120,18 +115,19 @@ void Config::Impl::read()
if (colon != ':')
continue;
std::string select_str;
while(line_stream >> select_str)
(lvl_fltrs_[lvl].*add_string)(select_str);
while (line_stream >> select_str)
((*lvl_fltrs_)[lvl].*add_string)(select_str);
}
return true;
}
int Config::Impl::level(const char* const _flnm, const char* const _fnct) const
int Config::custom_level(const char* const _flnm, const char* const _fnct) const
{
int lvl = dflt_lvl_;
for (LevelFilterMap::const_iterator fltr_it = lvl_fltrs_.begin();
fltr_it != lvl_fltrs_.end(); ++fltr_it)
if (lvl_fltrs_ == nullptr)
return output_level;
int lvl = output_level;
for (const auto& fltr : *lvl_fltrs_)
{// continue this iteration until the maximum allowed level if found
const LevelFilterMap::value_type& fltr = *fltr_it;
if (lvl >= fltr.first) // can this filter increase the current level?
continue;
if (fltr.second.select_file(_flnm) || fltr.second.select_function(_fnct))
......@@ -152,20 +148,15 @@ const Config& Config::query()
return modify();
}
Config::Config() : impl_(new Impl) {}
Config::~Config() { delete impl_; }
int Config::level(const char* const _flnm, const char* const _fnct) const
const Config& Config::defaults()
{
return impl_->level(_flnm, _fnct);
static Config dflt_cnfg;
return dflt_cnfg;
}
void Config::set_console(const bool _on) { File::modify().set_console(_on); }
bool Config::console() const { return File::query().console(); }
Config::Config() {}
void Config::set_logfile(bool _on) { File::modify().set_logfile(_on); }
bool Config::logfile() const { return File::query().logfile(); }
Config::~Config() { delete lvl_fltrs_; }
}//namespace Debug
......
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBCONFIG_HH_INCLUDED
#define BASE_DEBCONFIG_HH_INCLUDED
......@@ -6,10 +6,11 @@
#include <Base/Config/BaseDefines.hh>
#include <string>
#include <vector>
namespace Debug {
void print_char_to_cerr(const char _c); //!< print a char to cerr
/*!
Access the global, per-process, configuration options of the Debug system.
\todo Make this a per-thread configuration.
......@@ -17,28 +18,46 @@ Access the global, per-process, configuration options of the Debug system.
class BASEDLLEXPORT Config
{
public:
static const Config& query();
//! Define the function type to print a character on the console
typedef void (*print_function)(const char);
public:
//! Modify the current configuration.
static Config& modify();
//! Query the current configuration.
static const Config& query();
//! Query the default configuration.
static const Config& defaults();
public:
//! Turn on/off the console. This could be extended to redirect it.
void set_console(const bool _on = true);
//! The output level for all code in the absence of a config file.
int output_level = 5;
//! The deb out log filename, nullptr disables the debug output log file.
const char* log_filename = nullptr;
//! Get if the console is turned on/off.
bool console() const;
//! Function to deb out on the console, nullptr if output disabled.
print_function console_print = print_char_to_cerr;
//! Turn on/off the logfile. This could be extended to provide a FILE*.
void set_logfile(const bool _on);
public:
//! The output level for the given filename and function.
int custom_level(const char* const _flnm, const char* const _fnct) const;
//! Get if the logfile is turned on/off.
bool logfile() const;
/*!
Load the configuration file specified either by the environment variable
or the filename if the the environment variable is not set.
\todo Document the config format.
\return true if the configuration file was loaded properly, false otherwise.
*/
bool load(const char* const _cnfg_envr, const char* const _cnfg_flnm);
//! Get the level for this filename and function
int level(const char* const _flnm, const char* const _fnct) const;
private:
class LevelFilterMap;
private:
class Impl;
Impl* impl_;
LevelFilterMap* lvl_fltrs_ = nullptr;
private:
//! Private constructor
......
// (C) Copyright 2016 by Autodesk, Inc.
#ifndef BASE_DEBDEFAULT_HH_INCLUDED
#define BASE_DEBDEFAULT_HH_INCLUDED
namespace Debug {
namespace Default {
const char* const LOG_FILENAME = "reform_deb_out.txt";
const int LEVEL = 5;
}//Default
}//Debug
#endif//BASE_DEBDEFAULT_HH_INCLUDED
// (C) Copyright 2015 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBERROR_HH_INCLUDED
#define BASE_DEBERROR_HH_INCLUDED
......
This diff is collapsed.
// (C) Copyright 2016 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBFILE_HH_INCLUDED
#define BASE_DEBFILE_HH_INCLUDED
#ifdef DEB_ON
#include <vector>
#include <string>
#include <Base/Utils/IOutputStream.hh>
#include <Base/Debug/DebConfig.hh>
#include <string>
namespace Debug {
typedef unsigned int uint;
/*!
Debug file.
*/
class Enter;
//! Debug file.
class File
{
public:
......@@ -20,48 +22,42 @@ public:
static File& modify();
public:
enum Flags
{
APPEND = 0x01,
HTML = 0x02,
RETAIN = 0x04,
KEEP_OPEN = 0x08,
CONSOLE = 0x10,
LOGFILE = 0x20,
DEFAULT = APPEND | RETAIN | CONSOLE | LOGFILE
};
const char* double_format = "%.17g";
public:
File(const char* const _flnm, const uint _flags = DEFAULT);
~File();
bool no_log() const { return flnm_ == nullptr; }
bool log() const { return !no_log(); }
void enter(const int _id);
void enter(const Enter* const _entr);
void start();
void exit();
void print(const char _c);
void print(const char* _s);
void print(const char _c) { print(_c, true); }
void print(const char* const _s);
void print(const std::string& _s);
void print(const size_t _i);
void print(const int _i);
void print(double _d);
void print(const Base::Command& _co);
const char* double_format() const;
void set_double_format(const char* const str);
//! Turn on/off the console. This could be extended to redirect it.
void set_console(const bool _on = true);
private:
int flush_nmbr_ = 0;
bool line_strt_ = true; // are we at the start of the line?
Config::print_function cnsl_prnt_ = Config::query().console_print;
const char* const flnm_ = Config::query().log_filename;
std::string* bffr_ = nullptr;
//! Get if the console is turned on/off.
bool console() const;
private:
~File();
//! Turn on/off the logfile. This could be extended to provide a FILE*.
void set_logfile(const bool _on);
bool flushed() const { return flush_nmbr_ > 0; }
//! Get if the logfile is turned on/off.
bool logfile() const;
void print_header();
void print_footer();
void print_time();
private:
class Impl;
Impl* impl_;
void print(const char _c, const bool _cnsl);
void line_break(const bool _cnsl = true);
void flush();
};
}//namespace Debug
......
// (C) Copyright 2015 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#include "DebFileOut.hh"
......@@ -16,7 +16,7 @@ std::string make_filename(const char* _prfx, const char* _flnm,
std::stringstream sstr_flnm;
sstr_flnm << std::setfill('0') << std::setw(4) << cnt++;
sstr_flnm << SEP << _prfx << SEP << _flnm;
if (_sfx != NULL)
if (_sfx != nullptr)
sstr_flnm << SEP << _sfx;
sstr_flnm << '.' << _ext;
......
// (C) Copyright 2014 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBFILEOUT_HH_INCLUDED
#define BASE_DEBFILEOUT_HH_INCLUDED
......@@ -7,7 +7,6 @@
#include <string>
#include <Base/Config/BaseDefines.hh>
namespace Debug {
//! Make a file name composing the input arguments:
......@@ -16,7 +15,7 @@ namespace Debug {
// it is expressed with 4 decimal digits filled with zeros.
BASEDLLEXPORT
std::string make_filename(const char* _prfx, const char* _flnm,
const char* _ext, const char* _sfx = NULL);
const char* _ext, const char* _sfx = nullptr);
BASEDLLEXPORT
std::string set_filename_extension(const char* _flnm, const char* _ext);
......
// (C) Copyright 2014 by Autodesk, Inc.
// (C) Copyright 2019 by Autodesk, Inc.
#ifndef BASE_DEBOUT_HH_INCLUDED
#define BASE_DEBOUT_HH_INCLUDED
......@@ -8,7 +8,6 @@
// DEB_ON is defined, or not, in CMakeLists.txt for primary project
#ifndef DEB_ON
#define DEB_module(SS)
#define DEB_enter_func PROGRESS_TICK;
#define DEB_only(CC)
#define DEB_exec(LL, AA) { PROGRESS_TICK; }
......@@ -30,7 +29,6 @@
#include <string>
#include <sstream>
#include <vector>
// ERROR is occasionally defined in system headers, e.g., windows.h
#ifdef ERROR
......@@ -46,7 +44,7 @@ const char* const ERROR = "ERROR";
const char* const WARNING = "WARNING";
/*!
File is a private implementation for Stream, which is supplies data for several
File is a private implementation for Stream, which supplies data for several
other Debug and Test features.
*/
class File;
......@@ -113,8 +111,10 @@ private:
Stream strm_; //!< Stream
};
BASEDLLEXPORT extern void warning(const std::string& _wrng, const ::Base::CodeLink& _lnk);
BASEDLLEXPORT extern void error(const std::string& _err, const ::Base::CodeLink& _lnk);
BASEDLLEXPORT extern void warning(const std::string& _wrng,
const ::Base::CodeLink& _lnk);
BASEDLLEXPORT extern void error(const std::string& _err,
const ::Base::CodeLink& _lnk);
// TODO: Move this upstream to namespace Base?!
template <typename T>
......@@ -127,9 +127,6 @@ std::string to_string(const T& _t)
}//namespace Debug
// Obsolete, use gradually removed
#define DEB_module(MODULE)
//TODO: This should use an atomic thread-safe static int(s)
#define DEB_enter_func \
PROGRESS_TICK; \
......@@ -139,22 +136,32 @@ std::string to_string(const T& _t)
#define DEB_only(CC) CC
#define DEB_if(CC, LL, EXPR) \
{ \
PROGRESS_TICK; \
{ \
if (deb.pass(LL) && (CC)) \
{ \
EXPR; \