diff --git a/container/src/root/common/type.hh b/container/src/root/common/type.hh index a9be988..95650a8 100644 --- a/container/src/root/common/type.hh +++ b/container/src/root/common/type.hh @@ -24,32 +24,7 @@ #define CONTAINER_SRC_ROOT_COMMON_TYPE_HH_ #include -#include - -//! \brief Conditional throw handler -//! \ingroup cpp_type_exceptions -#define THROW_IF(condition, exception, message) \ - if (condition) \ - THROW(exception, message); - -//! \brief Throw handler -//! \ingroup cpp_type_exceptions -#define THROW(exception, message) \ - THROW_IMPL(exception, \ - \n\tFILE = __FILE__ \ - \n\tLINE = __LINE__ \ - \n\tWHAT = message); - -//! \brief Throw handler implementation -//! \warning Should not be called directly, use THROW handlers -//! \ingroup cpp_type_exceptions -#define THROW_IMPL(exception, message) \ - throw exception(THROW_IMPL_EXPAND(message)); - -//! \brief Throw handler implementation (message) -//! \warning Should not be called directly, use THROW handlers -//! \ingroup cpp_type_exceptions -#define THROW_IMPL_EXPAND(message) #message +#include //! \namespace dfi //! \since docker-finance 1.0.0 @@ -70,15 +45,15 @@ namespace type class Exception : virtual public std::exception { public: - //! \brief Exception type + //! \brief Exception type tag enum struct kType : uint8_t { RuntimeError, InvalidArgument, }; - //! \brief Construct by type with given message - Exception(const kType type, const std::string_view what) + //! \brief Construct by type tag with given message + Exception(const kType type, const std::string& what) : m_type(type), m_what(what) { } @@ -91,32 +66,32 @@ class Exception : virtual public std::exception Exception& operator=(Exception&&) = default; public: - //! \return Exeption type + //! \return Exeption type tag kType type() const noexcept { return m_type; } //! \return Exception message - const char* what() const noexcept { return m_what.data(); } + const char* what() const noexcept { return m_what.c_str(); } private: kType m_type; - std::string_view m_what; + std::string m_what; }; -//! \brief Exception class for runtime errors +//! \brief Exception type for runtime errors //! \ingroup cpp_type_exceptions struct RuntimeError final : public Exception { - explicit RuntimeError(const std::string_view what = {}) + explicit RuntimeError(const std::string& what = {}) : Exception(Exception::kType::RuntimeError, what) { } }; -//! \brief Exception class for invalid arguments (logic error) +//! \brief Exception type for invalid arguments (logic error) //! \ingroup cpp_type_exceptions struct InvalidArgument final : public Exception { - explicit InvalidArgument(const std::string_view what = {}) + explicit InvalidArgument(const std::string& what = {}) : Exception(Exception::kType::InvalidArgument, what) { } diff --git a/container/src/root/common/utility.hh b/container/src/root/common/utility.hh index 148146e..f3a385c 100644 --- a/container/src/root/common/utility.hh +++ b/container/src/root/common/utility.hh @@ -30,6 +30,8 @@ #include #include +#include "./type.hh" + //! \namespace dfi //! \since docker-finance 1.0.0 namespace dfi @@ -39,6 +41,51 @@ namespace dfi //! \since docker-finance 1.1.0 namespace common { + +//! \concept Exception +//! \brief Exception type concept for `dfi` exceptions +//! \ingroup cpp_common_impl +//! \since docker-finance 1.1.0 +template +concept Exception = std::derived_from; + +//! \brief Throw exception using `dfi` exception type +//! \param message Message to pass to exception type +//! \param location Source location of exception +//! \ingroup cpp_common_impl +//! \since docker-finance 1.1.0 +template +inline void throw_ex( + const std::string& message = {}, + const std::source_location location = std::source_location::current()) +{ + // TODO(unassigned): when throwing because of heap allocation + std::string msg; + msg += std::string(" \n\tFILE = ") + location.file_name(); + msg += std::string(" \n\tFUNC = ") + location.function_name(); + msg += std::string(" \n\tLINE = ") + location.line(); + msg += std::string(" \n\tWHAT = ") + message; + throw t_exception(msg); +} + +//! \brief Throw exception using `dfi` exception type (but only on condition) +//! \param condition If boolean condition is true, throw +//! \param message Message to pass to exception type +//! \param location Source location of exception +//! \ingroup cpp_common_impl +//! \since docker-finance 1.1.0 +template +inline void throw_ex_if( + const bool condition, + const std::string& message = {}, + const std::source_location location = std::source_location::current()) +{ + if (condition) + { + throw_ex(message, location); + } +} + //! \brief Wrapper to ROOT Cling commands //! \ingroup cpp_common_impl //! \since docker-finance 1.0.0 @@ -69,16 +116,13 @@ class Command static void load(const std::string& path) { std::filesystem::path p(path); - if (!std::filesystem::exists(p)) - { - throw std::runtime_error( - std::string{"'" + path + "' does not exist!"}.c_str()); - } - if (!std::filesystem::is_regular_file(p)) - { - throw std::runtime_error( - std::string{"'" + path + "' is not a regular file!"}.c_str()); - } + + throw_ex_if( + !std::filesystem::exists(p), "'" + path + "' does not exist!"); + + throw_ex_if( + !std::filesystem::is_regular_file(p), + "'" + path + "' is not a regular file!"); std::string cmd{".L " + path}; Command::cmd_handler({cmd}); @@ -101,9 +145,10 @@ class Command std::string get_env(const std::string& var) { const auto* env = gSystem->Getenv(var.c_str()); - if (!env) - throw std::runtime_error( - std::string{var + " is not set or is unavailable"}.c_str()); + + throw_ex_if( + !env, "'" + var + "' is not set or is unavailable"); + return std::string{env}; } diff --git a/container/src/root/macro/web/internal/meta.C b/container/src/root/macro/web/internal/meta.C index 2d1fed8..2e6dfae 100644 --- a/container/src/root/macro/web/internal/meta.C +++ b/container/src/root/macro/web/internal/meta.C @@ -184,7 +184,7 @@ class Meta final //! \param column Existing CSV metadata column static void meta_sample(const std::string& column) { - namespace common = ::dfi::macro::common; + namespace common = ::dfi::common; // Import meta file const std::string path{common::get_env("global_conf_meta")}; @@ -197,14 +197,12 @@ class Meta final available += "\n\t" + col; available += "\n"; - if (!csv->HasColumn(column)) - throw std::runtime_error( - std::string{"\n\nAvailable columns: " + available}.c_str()); + common::throw_ex_if( + !csv->HasColumn(column), "\n\nAvailable columns: " + available); // Aggregate all rows of given column auto count = csv->Count(); - if (!*count) - throw std::runtime_error("Empty CSV"); + common::throw_ex_if(!*count, "Empty CSV"); // Prepare canvas data Meta::CanvasData data; diff --git a/container/src/root/plugin/common/utility.hh b/container/src/root/plugin/common/utility.hh index a124f5d..498933d 100644 --- a/container/src/root/plugin/common/utility.hh +++ b/container/src/root/plugin/common/utility.hh @@ -87,7 +87,7 @@ void load(const std::string& path) } else { - throw std::runtime_error( + ::dfi::common::throw_ex<::dfi::common::type::RuntimeError>( "must be of type 'repo/' or 'custom/'"); } diff --git a/container/src/root/src/internal/impl/hash.hh b/container/src/root/src/internal/impl/hash.hh index a4a99e5..814c6cf 100644 --- a/container/src/root/src/internal/impl/hash.hh +++ b/container/src/root/src/internal/impl/hash.hh @@ -429,10 +429,8 @@ class Hash : public common::HashImpl, public type::Hash //! have any effects." Hash() { - THROW_IF( - ::sodium_init() < 0, - type::RuntimeError, - "sodium_init could not be initialized") + ::dfi::common::throw_ex_if<::dfi::common::type::RuntimeError>( + ::sodium_init() < 0, "sodium_init could not be initialized"); } ~Hash() = default; diff --git a/container/src/root/src/internal/impl/random.hh b/container/src/root/src/internal/impl/random.hh index 837823e..6819f36 100644 --- a/container/src/root/src/internal/impl/random.hh +++ b/container/src/root/src/internal/impl/random.hh @@ -128,7 +128,8 @@ class Random : public common::RandomImpl || std::is_same_v, "Invalid type (only uint16_t or uint32_t supported)"); - THROW_IF(!m_csprng.is_seeded(), type::RuntimeError, "Botan is not seeded") + ::dfi::common::throw_ex_if( + !m_csprng.is_seeded(), "Botan is not seeded"); // WARNING: DO *NOT* set_high_bit to true here! // Otherwise, [0..(~2150*10^6)] WILL NOT BE GENERATED! @@ -222,10 +223,8 @@ class Random : public common::RandomImpl //! have any effects." Random() { - THROW_IF( - ::sodium_init() < 0, - type::RuntimeError, - "sodium_init could not be initialized") + ::dfi::common::throw_ex_if<::dfi::common::type::RuntimeError>( + ::sodium_init() < 0, "sodium_init could not be initialized"); } ~Random() = default; diff --git a/container/src/root/src/internal/impl/utility.hh b/container/src/root/src/internal/impl/utility.hh index 3fd912c..a253fdd 100644 --- a/container/src/root/src/internal/impl/utility.hh +++ b/container/src/root/src/internal/impl/utility.hh @@ -159,8 +159,8 @@ class Tools std::vector random_dist(const t_num min, const t_num max, const uint16_t precision = 8) { - THROW_IF( - min > max, type::InvalidArgument, "minimum is greater than maximum") + ::dfi::common::throw_ex_if<::dfi::common::type::InvalidArgument>( + min > max, "minimum is greater than maximum"); // Get first chunk std::vector chunks; @@ -193,7 +193,8 @@ class Tools std::cout.precision(precision); std::cout << "rounded = " << rounded << '\n' << "max = " << max << std::fixed << std::endl; - THROW(type::RuntimeError, "random distribution not fulfilled") + ::dfi::common::throw_ex<::dfi::common::type::RuntimeError>( + "random distribution not fulfilled"); } return chunks; diff --git a/container/src/root/src/utility.hh b/container/src/root/src/utility.hh index e8a635e..4f6e266 100644 --- a/container/src/root/src/utility.hh +++ b/container/src/root/src/utility.hh @@ -43,6 +43,7 @@ namespace dfi namespace utility { +namespace common = ::dfi::common; namespace type = dfi::internal::type; //! \brief Misc utility tools @@ -86,8 +87,8 @@ class Tools final : protected impl::Tools void print_dist(t_num min, t_num max, const size_t precision = 8) { // Specific to this use-case - THROW_IF( - min < 0 || max < 0, type::InvalidArgument, "no negative values allowed") + common::throw_ex_if( + min < 0 || max < 0, "no negative values allowed"); if (min > max) { diff --git a/container/src/root/test/common/type.hh b/container/src/root/test/common/type.hh index 0a4b3d0..9a6fb24 100644 --- a/container/src/root/test/common/type.hh +++ b/container/src/root/test/common/type.hh @@ -25,7 +25,7 @@ #include -#include +#include #include "../../src/internal/type.hh" @@ -50,8 +50,8 @@ namespace unit struct Exception : public ::testing::Test { protected: - std::string_view what = "better to be caught than thrown"; - using t_type = type::Exception::kType; + std::string what = "better to be caught than thrown"; + using t_type = ::dfi::common::type::Exception::kType; }; } // namespace unit } // namespace tests diff --git a/container/src/root/test/unit/type.hh b/container/src/root/test/unit/type.hh index 414a3f8..15d30aa 100644 --- a/container/src/root/test/unit/type.hh +++ b/container/src/root/test/unit/type.hh @@ -46,6 +46,8 @@ namespace tests namespace unit { +namespace common = ::dfi::common; + // // RuntimeError // @@ -56,11 +58,11 @@ struct RuntimeError : public Exception { }; -TEST_F(RuntimeError, macro_THROW_IF) // cppcheck-suppress syntaxError +TEST_F(RuntimeError, throw_ex_if) // cppcheck-suppress syntaxError { try { - THROW_IF(false, type::RuntimeError, message); + common::throw_ex_if(false, what); } catch (const type::Exception& ex) { @@ -69,16 +71,16 @@ TEST_F(RuntimeError, macro_THROW_IF) // cppcheck-suppress syntaxError } } -TEST_F(RuntimeError, macro_THROW) +TEST_F(RuntimeError, throw_ex) { try { - THROW(type::RuntimeError, message); + common::throw_ex(what); } catch (const type::Exception& ex) { ASSERT_EQ(ex.type(), t_type::RuntimeError); - // NOTE: no case for ex.what() because message is formatted within macro + // NOTE: no case for ex.what() because message is formatted within function } } @@ -108,7 +110,7 @@ TEST_F(RuntimeError, copy_assignment) two = one; ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STREQ(one.what(), two.what()); } TEST_F(RuntimeError, copy_ctor) @@ -117,7 +119,7 @@ TEST_F(RuntimeError, copy_ctor) type::RuntimeError two(one); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STREQ(one.what(), two.what()); } TEST_F(RuntimeError, move_assignment) @@ -128,7 +130,7 @@ TEST_F(RuntimeError, move_assignment) two = std::move(one); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STRNE(one.what(), two.what()); } TEST_F(RuntimeError, move_ctor) @@ -137,7 +139,7 @@ TEST_F(RuntimeError, move_ctor) type::RuntimeError two(std::move(one)); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STRNE(one.what(), two.what()); } // @@ -150,11 +152,11 @@ struct InvalidArgument : public Exception { }; -TEST_F(InvalidArgument, macro_THROW_IF) // cppcheck-suppress syntaxError +TEST_F(InvalidArgument, throw_ex_if) { try { - THROW_IF(false, type::InvalidArgument, message); + common::throw_ex_if(false, what); } catch (const type::Exception& ex) { @@ -163,16 +165,16 @@ TEST_F(InvalidArgument, macro_THROW_IF) // cppcheck-suppress syntaxError } } -TEST_F(InvalidArgument, macro_THROW) +TEST_F(InvalidArgument, throw_ex) { try { - THROW(type::InvalidArgument, message); + common::throw_ex(what); } catch (const type::Exception& ex) { ASSERT_EQ(ex.type(), t_type::InvalidArgument); - // NOTE: no case for ex.what() because message is formatted within macro + // NOTE: no case for ex.what() because message is formatted within function } } @@ -202,7 +204,7 @@ TEST_F(InvalidArgument, copy_assignment) two = one; ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STREQ(one.what(), two.what()); } TEST_F(InvalidArgument, copy_ctor) @@ -211,7 +213,7 @@ TEST_F(InvalidArgument, copy_ctor) type::InvalidArgument two(one); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STREQ(one.what(), two.what()); } TEST_F(InvalidArgument, move_assignment) @@ -222,7 +224,7 @@ TEST_F(InvalidArgument, move_assignment) two = std::move(one); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STRNE(one.what(), two.what()); } TEST_F(InvalidArgument, move_ctor) @@ -231,7 +233,7 @@ TEST_F(InvalidArgument, move_ctor) type::InvalidArgument two(std::move(one)); ASSERT_EQ(one.type(), two.type()); - ASSERT_EQ(one.what(), two.what()); + ASSERT_STRNE(one.what(), two.what()); } } // namespace unit diff --git a/container/src/root/test/unit/utility.hh b/container/src/root/test/unit/utility.hh index e6f2dee..153cc10 100644 --- a/container/src/root/test/unit/utility.hh +++ b/container/src/root/test/unit/utility.hh @@ -60,6 +60,9 @@ namespace tests //! \since docker-finance 1.0.0 namespace unit { + +namespace common = ::dfi::common; + //! \brief Tools utility fixture //! \since docker-finance 1.0.0 struct Tools : public ::testing::Test, public tests::ToolsFixture @@ -296,50 +299,41 @@ struct CommonRawFiles : public ::testing::Test, } catch (...) { - THROW(std::runtime_error, "could not generate path"); + common::throw_ex("could not generate path"); } std::ofstream file_1(path_1), file_2(path_2); - THROW_IF( - !file_1.is_open() || !file_2.is_open(), - std::runtime_error, - "could not create file"); + common::throw_ex_if( + !file_1.is_open() || !file_2.is_open(), "could not create file"); file_1 << "using my_foo = int;\n"; file_1.close(); file_2 << "using my_bar = char;\n"; file_2.close(); - THROW_IF( - file_1.bad() || file_2.bad(), - std::runtime_error, - "could not write to file"); + common::throw_ex_if( + file_1.bad() || file_2.bad(), "could not write to file"); } void TearDown() override { - THROW_IF( - std::remove(path_1.c_str()), - std::runtime_error, - "could not remove file '" + path_1 + "'"); + common::throw_ex_if( + std::remove(path_1.c_str()), "could not remove file '" + path_1 + "'"); - THROW_IF( - std::remove(path_2.c_str()), - std::runtime_error, - "could not remove file '" + path_2 + "'"); + common::throw_ex_if( + std::remove(path_2.c_str()), "could not remove file '" + path_2 + "'"); } }; TEST_F(CommonRawFiles, RawLoadSingle) { ASSERT_THROW( - ::dfi::common::Command::load({path_1 + "should-not-exist"}), - std::runtime_error); + common::Command::load({path_1 + "should-not-exist"}), type::RuntimeError); gInterpreter->ProcessLine("my_foo foo;", ecode.get()); ASSERT_NE(ecode, nullptr); EXPECT_NE(*ecode, EErrorCode::kNoError); - ASSERT_NO_THROW(::dfi::common::Command::load(path_1)); + ASSERT_NO_THROW(common::Command::load(path_1)); gInterpreter->ProcessLine("my_foo foo;", ecode.get()); ASSERT_NE(ecode, nullptr); EXPECT_EQ(*ecode, EErrorCode::kNoError); @@ -348,14 +342,14 @@ TEST_F(CommonRawFiles, RawLoadSingle) TEST_F(CommonRawFiles, RawLoadMultiple) { ASSERT_THROW( - ::dfi::common::Command::load( + common::Command::load( {{path_1 + "should-not-exist"}, {path_2 + "nor-should-this"}}), - std::runtime_error); + type::RuntimeError); gInterpreter->ProcessLine("my_bar bar;", ecode.get()); ASSERT_NE(ecode, nullptr); EXPECT_NE(*ecode, EErrorCode::kNoError); - ASSERT_NO_THROW(::dfi::common::Command::load({path_1, path_2})); + ASSERT_NO_THROW(common::Command::load({path_1, path_2})); gInterpreter->ProcessLine("my_bar bar;", ecode.get()); ASSERT_NE(ecode, nullptr); EXPECT_EQ(*ecode, EErrorCode::kNoError); @@ -373,7 +367,7 @@ struct CommonRepoFiles : public ::testing::Test, TEST_F(CommonRepoFiles, MacroLoad) { ASSERT_THROW( - ::dfi::macro::load("macro/should-not/exist.C"), std::runtime_error); + ::dfi::macro::load("macro/should-not/exist.C"), type::RuntimeError); gInterpreter->ProcessLine( "dfi::macro::common::crypto::botan::Hash h;", ecode.get()); ASSERT_NE(ecode, nullptr); @@ -391,7 +385,7 @@ TEST_F(CommonRepoFiles, MacroLoad) TEST_F(CommonRepoFiles, PluginLoad) { ASSERT_THROW( - ::dfi::plugin::load("repo/should-not/exist.cc"), std::runtime_error); + ::dfi::plugin::load("repo/should-not/exist.cc"), type::RuntimeError); gInterpreter->ProcessLine( "dfi::plugin::my_plugin_namespace::example2();", ecode.get()); ASSERT_NE(ecode, nullptr); @@ -416,19 +410,19 @@ struct CommonFree : public ::testing::Test, public ::dfi::tests::CommonFixture TEST_F(CommonFree, env) { - ASSERT_THROW(::dfi::common::get_env("should-not-exist"), std::runtime_error); - ASSERT_NO_THROW(::dfi::common::get_env("DOCKER_FINANCE_VERSION")); + ASSERT_THROW(common::get_env("should-not-exist"), type::RuntimeError); + ASSERT_NO_THROW(common::get_env("DOCKER_FINANCE_VERSION")); } TEST_F(CommonFree, exec) { - ASSERT_NE(::dfi::common::exec("should-not-exist"), 0); - ASSERT_EQ(::dfi::common::exec("pwd"), 0); + ASSERT_NE(common::exec("should-not-exist"), 0); + ASSERT_EQ(common::exec("pwd"), 0); } TEST_F(CommonFree, make_timestamp) { - ASSERT_EQ(::dfi::common::make_timestamp().size(), 20); + ASSERT_EQ(common::make_timestamp().size(), 20); } } // namespace unit