Files
geant4/source/externals/ptl/include/PTL/Utility.hh
T
2022-12-09 14:43:28 +01:00

403 lines
13 KiB
C++

//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Global utility functions
//
#pragma once
#include <cctype>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <set>
#include <sstream> // IWYU pragma: keep
#include <string>
#include <tuple>
#include <utility>
namespace PTL
{
//--------------------------------------------------------------------------------------//
// use this function to get rid of "unused parameter" warnings
//
template <typename... Args>
void
ConsumeParameters(Args&&...)
{}
//--------------------------------------------------------------------------------------//
// a non-string environment option with a string identifier
template <typename Tp>
using EnvChoice = std::tuple<Tp, std::string, std::string>;
//--------------------------------------------------------------------------------------//
// list of environment choices with non-string and string identifiers
template <typename Tp>
using EnvChoiceList = std::set<EnvChoice<Tp>>;
//--------------------------------------------------------------------------------------//
class EnvSettings
{
public:
using mutex_t = std::mutex;
using string_t = std::string;
using env_map_t = std::multimap<string_t, string_t>;
using env_pair_t = std::pair<string_t, string_t>;
public:
static EnvSettings* GetInstance()
{
static EnvSettings* _instance = new EnvSettings();
return _instance;
}
public:
template <typename Tp>
void insert(const std::string& env_id, Tp val)
{
std::stringstream ss;
ss << std::boolalpha << val;
m_mutex.lock();
if(m_env.find(env_id) != m_env.end())
{
for(const auto& itr : m_env)
if(itr.first == env_id && itr.second == ss.str())
{
m_mutex.unlock();
return;
}
}
m_env.insert(env_pair_t(env_id, ss.str()));
m_mutex.unlock();
}
template <typename Tp>
void insert(const std::string& env_id, EnvChoice<Tp> choice)
{
Tp& val = std::get<0>(choice);
std::string& str_val = std::get<1>(choice);
std::string& descript = std::get<2>(choice);
std::stringstream ss, ss_long;
ss << std::boolalpha << val;
ss_long << std::boolalpha << std::setw(8) << std::left << val << " # (\""
<< str_val << "\") " << descript;
m_mutex.lock();
if(m_env.find(env_id) != m_env.end())
{
for(const auto& itr : m_env)
if(itr.first == env_id && itr.second == ss.str())
{
m_mutex.unlock();
return;
}
}
m_env.insert(env_pair_t(env_id, ss_long.str()));
m_mutex.unlock();
}
const env_map_t& get() const { return m_env; }
mutex_t& mutex() const { return m_mutex; }
friend std::ostream& operator<<(std::ostream& os, const EnvSettings& env)
{
std::stringstream filler;
filler.fill('#');
filler << std::setw(90) << "";
std::stringstream ss;
ss << filler.str() << "\n# Environment settings:\n";
env.mutex().lock();
for(const auto& itr : env.get())
{
ss << "# " << std::setw(35) << std::right << itr.first << "\t = \t"
<< std::left << itr.second << "\n";
}
env.mutex().unlock();
ss << filler.str();
os << ss.str() << std::endl;
return os;
}
private:
env_map_t m_env;
mutable mutex_t m_mutex;
};
//--------------------------------------------------------------------------------------//
// use this function to get an environment variable setting +
// a default if not defined, e.g.
// int num_threads =
// GetEnv<int>("FORCENUMBEROFTHREADS",
// std::thread::hardware_concurrency());
//
template <typename Tp>
Tp
GetEnv(const std::string& env_id, Tp _default = Tp())
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::istringstream iss(str_var);
Tp var = Tp();
iss >> var;
// record value defined by environment
EnvSettings::GetInstance()->insert<Tp>(env_id, var);
return var;
}
// record default value
EnvSettings::GetInstance()->insert<Tp>(env_id, _default);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// overload for boolean
//
template <>
inline bool
GetEnv(const std::string& env_id, bool _default)
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string var = std::string(env_var);
bool val = true;
if(var.find_first_not_of("0123456789") == std::string::npos)
val = (bool) atoi(var.c_str());
else
{
for(auto& itr : var)
itr = (char)std::tolower(itr);
if(var == "off" || var == "false")
val = false;
}
// record value defined by environment
EnvSettings::GetInstance()->insert<bool>(env_id, val);
return val;
}
// record default value
EnvSettings::GetInstance()->insert<bool>(env_id, false);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// overload for GetEnv + message when set
//
template <typename Tp>
Tp
GetEnv(const std::string& env_id, Tp _default, const std::string& msg)
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::istringstream iss(str_var);
Tp var = Tp();
iss >> var;
std::cout << "Environment variable \"" << env_id << "\" enabled with "
<< "value == " << var << ". " << msg << std::endl;
// record value defined by environment
EnvSettings::GetInstance()->insert<Tp>(env_id, var);
return var;
}
// record default value
EnvSettings::GetInstance()->insert<Tp>(env_id, _default);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// use this function to get an environment variable setting from set of choices
//
// EnvChoiceList<int> choices =
// { EnvChoice<int>(NN, "NN", "nearest neighbor interpolation"),
// EnvChoice<int>(LINEAR, "LINEAR", "bilinear interpolation"),
// EnvChoice<int>(CUBIC, "CUBIC", "bicubic interpolation") };
//
// int eInterp = GetEnv<int>("INTERPOLATION", choices, CUBIC);
//
template <typename Tp>
Tp
GetEnv(const std::string& env_id, const EnvChoiceList<Tp>& _choices, Tp _default)
{
auto asupper = [](std::string var) {
for(auto& itr : var)
itr = (char)std::toupper(itr);
return var;
};
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::string upp_var = asupper(str_var);
Tp var = Tp();
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(asupper(std::get<1>(itr)) == upp_var)
{
// record value defined by environment
EnvSettings::GetInstance()->insert(env_id, itr);
return std::get<0>(itr);
}
}
std::istringstream iss(str_var);
iss >> var;
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(var == std::get<0>(itr))
{
// record value defined by environment
EnvSettings::GetInstance()->insert(env_id, itr);
return var;
}
}
// the value set in env did not match any choices
std::stringstream ss;
ss << "\n### Environment setting error @ " << __FUNCTION__ << " (line "
<< __LINE__ << ")! Invalid selection for \"" << env_id
<< "\". Valid choices are:\n";
for(const auto& itr : _choices)
ss << "\t\"" << std::get<0>(itr) << "\" or \"" << std::get<1>(itr) << "\" ("
<< std::get<2>(itr) << ")\n";
std::cerr << ss.str() << std::endl;
abort();
}
std::string _name = "???";
std::string _desc = "description not provided";
for(const auto& itr : _choices)
if(std::get<0>(itr) == _default)
{
_name = std::get<1>(itr);
_desc = std::get<2>(itr);
break;
}
// record default value
EnvSettings::GetInstance()->insert(env_id, EnvChoice<Tp>(_default, _name, _desc));
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
template <typename Tp>
Tp
GetChoice(const EnvChoiceList<Tp>& _choices, const std::string& str_var)
{
auto asupper = [](std::string var) {
for(auto& itr : var)
itr = (char)std::toupper(itr);
return var;
};
std::string upp_var = asupper(str_var);
Tp var = Tp();
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(asupper(std::get<1>(itr)) == upp_var)
{
// record value defined by environment
return std::get<0>(itr);
}
}
std::istringstream iss(str_var);
iss >> var;
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(var == std::get<0>(itr))
{
// record value defined by environment
return var;
}
}
// the value set in env did not match any choices
std::stringstream ss;
ss << "\n### Environment setting error @ " << __FUNCTION__ << " (line " << __LINE__
<< ")! Invalid selection \"" << str_var << "\". Valid choices are:\n";
for(const auto& itr : _choices)
ss << "\t\"" << std::get<0>(itr) << "\" or \"" << std::get<1>(itr) << "\" ("
<< std::get<2>(itr) << ")\n";
std::cerr << ss.str() << std::endl;
abort();
}
//--------------------------------------------------------------------------------------//
inline void
PrintEnv(std::ostream& os = std::cout)
{
os << (*EnvSettings::GetInstance());
}
//--------------------------------------------------------------------------------------//
struct ScopeDestructor
{
template <typename FuncT>
ScopeDestructor(FuncT&& _func)
: m_functor(std::forward<FuncT>(_func))
{}
// delete copy operations
ScopeDestructor(const ScopeDestructor&) = delete;
ScopeDestructor& operator=(const ScopeDestructor&) = delete;
// allow move operations
ScopeDestructor(ScopeDestructor&& rhs) noexcept
: m_functor(std::move(rhs.m_functor))
{
rhs.m_functor = []() {};
}
ScopeDestructor& operator=(ScopeDestructor&& rhs) noexcept
{
if(this != &rhs)
{
m_functor = std::move(rhs.m_functor);
rhs.m_functor = []() {};
}
return *this;
}
~ScopeDestructor() { m_functor(); }
private:
std::function<void()> m_functor = []() {};
};
//--------------------------------------------------------------------------------------//
} // namespace PTL