Merge pull request #89 into master

de81eba container: root: macro: rootlogon.C: update usage (Aaron Fiore)
8d2389b container: root: refactor using internal throw/exception handler (Aaron Fiore)
9873e63 container: root: add unit test for internal throw/exception handler (Aaron Fiore)
612f9e5 container: root: add internal throw/exception handler (Aaron Fiore)
This commit was merged in pull request #89.
This commit is contained in:
2024-07-18 16:29:03 -07:00
11 changed files with 442 additions and 41 deletions

View File

@@ -80,6 +80,10 @@
//! \brief Group for tags marked for internal and public consumption
//! \since docker-finance 1.0.0
//! \defgroup cpp_type_exceptions docker-finance C++ exceptions
//! \brief Group for internal exception handling
//! \since docker-finance 1.0.0
//! \defgroup cpp_utils docker-finance C++ utilities
//! \brief Group for public-facing utility code
//! \since docker-finance 1.0.0

View File

@@ -60,39 +60,39 @@ void help()
<< " of Crypto++-generated cryptographically secure random\n"
<< " numbers:\n"
<< "\n"
<< " root [0] #include \"../src/hash.hh\"\n"
<< " root [1] #include \"../src/random.hh\"\n"
<< " root [2] using g_Random = "
<< " root [0] using g_Random = "
"docker_finance::crypto::cryptopp::Random;\n"
<< " root [3] using g_Hash = "
<< " root [1] using g_Hash = "
"docker_finance::crypto::libsodium::Hash;\n"
<< " root [4] g_Random r; g_Hash h;\n"
<< " root [5] for (size_t i{}; i < "
<< " root [2] g_Random r; g_Hash h;\n"
<< " root [3] for (size_t i{}; i < "
"std::numeric_limits<uint8_t>::max(); i++) {\n"
<< " root (cont'ed, cancel with .@) [6] uint32_t num = "
<< " root (cont'ed, cancel with .@) [4] uint32_t num = "
"r.generate();\n"
<< " root (cont'ed, cancel with .@) [7] std::cout << "
"h.encode<g_Hash::SHA2_256>(num) << \" = \" << num << \"\\n\"; }\n"
<< " root (cont'ed, cancel with .@) [5] std::cout << "
"h.encode<g_Hash::SHA2_256>(num)\n"
<< " root (cont'ed, cancel with .@) [6] << \" = \" << num << "
"\"\\n\";\n"
<< " root (cont'ed, cancel with .@) [7] }\n"
<< "\n"
<< " Note: generate Doxygen to see all supported cryptographic\n"
<< " libraries and hash types.\n"
<< "\n"
<< " 2. Use Tools utility\n"
<< "\n"
<< " root [0] #include \"../src/utility.hh\"\n"
<< " root [1] docker_finance::utility::Tools tools;\n"
<< " root [0] docker_finance::utility::Tools tools;\n"
<< "\n"
<< " // Create a variable within interpreter\n"
<< " root [2] btc=0.87654321+0.12345678+0.00000078\n"
<< " root [1] btc=0.87654321+0.12345678+0.00000078\n"
<< " (double) 1.0000008\n"
<< "\n"
<< " // Print variable up to N decimal places\n"
<< " root [3] tools.print_value<double>(btc, 8);\n"
<< " root [2] tools.print_value<double>(btc, 8);\n"
<< " 1.00000077\n"
<< "\n"
<< " // Use tab autocomplete for class members to print random\n"
<< " // numbers within given intervals\n"
<< " root [4] "
<< " root [3] "
"tools.print_dist<TRandomGen<ROOT::Math::MixMaxEngine<240, 0> >, "
"double>(0.1, btc);\n"
<< " 0.13787670\n"
@@ -121,7 +121,7 @@ void help()
<< "Help:\n"
<< "\n"
<< " 1. Print ROOT's help usage\n"
<< " root [1] .help\n"
<< " root [0] .help\n"
<< "\n"
<< " 2. Print this help usage\n"
<< " root [0] help()\n"

View File

@@ -63,8 +63,9 @@ class Unit
{
static bool loaded{false};
static const std::initializer_list<std::string> paths{
{"../test/unit/random.hh"},
{"../test/unit/hash.hh"},
{"../test/unit/random.hh"},
{"../test/unit/type.hh"},
{"../test/unit/utility.hh"}};
if (!loaded)

View File

@@ -29,8 +29,6 @@
#include "./internal/impl/hash.hh"
#include "./internal/type.hh"
namespace type = docker_finance::internal::type;
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
@@ -40,6 +38,9 @@ namespace docker_finance
//! \since docker-finance 1.0.0
namespace crypto
{
namespace type = docker_finance::internal::type;
//! \namespace docker_finance::crypto::common
//! \brief Common "interface" (specializations) to library-specific implementations
//! \warning Not for direct public consumption (use library namespace instead)

View File

@@ -44,8 +44,6 @@
#include "../generic.hh"
#include "../type.hh"
namespace type = docker_finance::internal::type;
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
@@ -60,6 +58,9 @@ namespace crypto
//! \since docker-finance 1.0.0
namespace impl
{
namespace type = docker_finance::internal::type;
//! \namespace docker_finance::crypto::impl::common
//! \brief Common implementation among all crypto implementations
//! \since docker-finance 1.0.0
@@ -428,11 +429,11 @@ class Hash : public common::HashImpl<libsodium::Hash>, public type::Hash
//! have any effects."
Hash()
{
if (::sodium_init() < 0)
{
throw std::runtime_error("sodium_init could not be initialized");
}
};
THROW_IF(
::sodium_init() < 0,
type::RuntimeError,
"sodium_init could not be initialized")
}
~Hash() = default;
Hash(const Hash&) = default;

View File

@@ -33,6 +33,7 @@
#include <type_traits>
#include "../generic.hh"
#include "../type.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
@@ -82,6 +83,8 @@ class RandomImpl : public ::docker_finance::internal::Random<t_impl>
};
} // namespace common
namespace type = docker_finance::internal::type;
//! \namespace docker_finance::crypto::impl::botan
//! \since docker-finance 1.0.0
namespace botan
@@ -117,9 +120,7 @@ class Random : public common::RandomImpl<botan::Random>
static_assert(
std::is_same_v<t_random, uint32_t>, "Random interface has changed");
if (!m_csprng.is_seeded())
throw std::runtime_error(
"Botan is not seeded"); // TODO(afiore): use docker-finance's error.hh
THROW_IF(!m_csprng.is_seeded(), type::RuntimeError, "Botan is not seeded")
// WARNING: DO *NOT* set_high_bit to true here!
// Otherwise, [0..(~2150*10^6)] WILL NOT BE GENERATED!
@@ -206,11 +207,11 @@ class Random : public common::RandomImpl<libsodium::Random>
//! have any effects."
Random()
{
if (::sodium_init() < 0)
{
throw std::runtime_error("sodium_init could not be initialized");
}
};
THROW_IF(
::sodium_init() < 0,
type::RuntimeError,
"sodium_init could not be initialized")
}
~Random() = default;
Random(const Random&) = default;

View File

@@ -31,6 +31,7 @@
// #include <calc/core.h> // TODO(afiore): file upstream (calc) bug report
#include "../../random.hh"
#include "../generic.hh"
#include "../type.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
@@ -46,6 +47,9 @@ namespace utility
//! \since docker-finance 1.0.0
namespace impl
{
namespace type = docker_finance::internal::type;
//! \brief Misc tools
//! \ingroup cpp_API_impl
//! \since docker-finance 1.0.0
@@ -155,8 +159,8 @@ class Tools
std::vector<t_num>
random_dist(const t_num min, const t_num max, const uint16_t precision = 8)
{
if (min > max)
throw std::runtime_error("minimum given is greater than maximum");
THROW_IF(
min > max, type::InvalidArgument, "minimum is greater than maximum")
// Get first chunk
std::vector<t_num> chunks;
@@ -189,8 +193,7 @@ class Tools
std::cout.precision(precision);
std::cout << "rounded = " << rounded << '\n'
<< "max = " << max << std::fixed << std::endl;
throw std::runtime_error("random distribution not fulfilled");
THROW(type::RuntimeError, "random distribution not fulfilled")
}
return chunks;

View File

@@ -25,10 +25,36 @@
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
//! \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
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
@@ -44,6 +70,63 @@ namespace internal
//! \todo *_v for all the booleans
namespace type
{
//! \brief Base exception class
//! \ingroup cpp_type_exceptions
class Exception : virtual public std::exception
{
public:
//! \brief Exception type
enum struct kType : uint8_t
{
RuntimeError,
InvalidArgument,
};
//! \brief Construct by type with given message
Exception(const kType type, const std::string_view what)
: m_type(type), m_what(what)
{
}
virtual ~Exception() = default;
Exception(const Exception&) = default;
Exception& operator=(const Exception&) = default;
Exception(Exception&&) = default;
Exception& operator=(Exception&&) = default;
public:
//! \return Exeption type
kType type() const noexcept { return m_type; }
//! \return Exception message
const char* what() const noexcept { return m_what.data(); }
private:
kType m_type;
std::string_view m_what;
};
//! \brief Exception class for runtime errors
//! \ingroup cpp_type_exceptions
struct RuntimeError final : public Exception
{
explicit RuntimeError(const std::string_view what = {})
: Exception(Exception::kType::RuntimeError, what)
{
}
};
//! \brief Exception class for invalid arguments (logic error)
//! \ingroup cpp_type_exceptions
struct InvalidArgument final : public Exception
{
explicit InvalidArgument(const std::string_view what = {})
: Exception(Exception::kType::InvalidArgument, what)
{
}
};
//! \ingroup cpp_type_traits
template <typename t_type>
struct is_byte : std::false_type

View File

@@ -31,8 +31,7 @@
#include <vector>
#include "./internal/impl/utility.hh"
namespace type = docker_finance::internal::type;
#include "./internal/type.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
@@ -43,6 +42,9 @@ namespace docker_finance
//! \since docker-finance 1.0.0
namespace utility
{
namespace type = docker_finance::internal::type;
//! \brief Misc utility tools
//! \ingroup cpp_utils
//! \since docker-finance 1.0.0
@@ -84,8 +86,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
if (min < 0 || max < 0)
throw std::runtime_error("No negative values allowed");
THROW_IF(
min < 0 || max < 0, type::InvalidArgument, "no negative values allowed")
if (min > max)
{

View File

@@ -0,0 +1,62 @@
// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! \file
//! \author Aaron Fiore (Founder, Evergreen Crypto LLC)
//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter
//! \since docker-finance 1.0.0
#ifndef CONTAINER_SRC_ROOT_TEST_COMMON_TYPE_HH_
#define CONTAINER_SRC_ROOT_TEST_COMMON_TYPE_HH_
#include <gtest/gtest.h>
#include <string_view>
#include "../../src/internal/type.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
{
//! \namespace docker_finance::tests
//! \brief docker-finance common test framework
//! \ingroup cpp_tests
//! \since docker-finance 1.0.0
namespace tests
{
//! \namespace docker_finance::tests::unit
//! \brief docker-finance unit test cases
//! \ingroup cpp_tests
//! \since docker-finance 1.0.0
namespace unit
{
//! \brief Exception fixture (base)
//! \since docker-finance 1.0.0
struct Exception : public ::testing::Test
{
protected:
std::string_view what = "better to be caught than thrown";
using t_type = type::Exception::kType;
};
} // namespace unit
} // namespace tests
} // namespace docker_finance
#endif // CONTAINER_SRC_ROOT_TEST_COMMON_TYPE_HH_
// # vim: sw=2 sts=2 si ai et

View File

@@ -0,0 +1,243 @@
// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! \file
//! \author Aaron Fiore (Founder, Evergreen Crypto LLC)
//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter
//! \since docker-finance 1.0.0
#ifndef CONTAINER_SRC_ROOT_TEST_UNIT_TYPE_HH_
#define CONTAINER_SRC_ROOT_TEST_UNIT_TYPE_HH_
#include <gtest/gtest.h>
#include <utility>
#include "../common/type.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
{
//! \namespace docker_finance::tests
//! \brief docker-finance common test framework
//! \ingroup cpp_tests
//! \since docker-finance 1.0.0
namespace tests
{
//! \namespace docker_finance::tests::unit
//! \brief docker-finance unit test cases
//! \ingroup cpp_tests
//! \since docker-finance 1.0.0
namespace unit
{
//
// RuntimeError
//
//! \brief Exception fixture (RuntimeError)
//! \since docker-finance 1.0.0
struct RuntimeError : public Exception
{
};
TEST_F(RuntimeError, macro_THROW_IF) // cppcheck-suppress syntaxError
{
try
{
THROW_IF(false, type::RuntimeError, message);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::RuntimeError);
ASSERT_EQ(ex.what(), what);
}
}
TEST_F(RuntimeError, macro_THROW)
{
try
{
THROW(type::RuntimeError, message);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::RuntimeError);
// NOTE: no case for ex.what() because message is formatted within macro
}
}
TEST_F(RuntimeError, rethrow)
{
ASSERT_THROW(
{
try
{
throw type::RuntimeError(what);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::RuntimeError);
ASSERT_EQ(ex.what(), what);
throw;
}
},
type::Exception);
}
TEST_F(RuntimeError, copy_assignment)
{
type::RuntimeError one(what);
type::RuntimeError two;
two = one;
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(RuntimeError, copy_ctor)
{
type::RuntimeError one(what);
type::RuntimeError two(one);
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(RuntimeError, move_assignment)
{
type::RuntimeError one(what);
type::RuntimeError two;
two = std::move(one);
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(RuntimeError, move_ctor)
{
type::RuntimeError one(what);
type::RuntimeError two(std::move(one));
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
//
// InvalidArgument
//
//! \brief Exception fixture (InvalidArgument)
//! \since docker-finance 1.0.0
struct InvalidArgument : public Exception
{
};
TEST_F(InvalidArgument, macro_THROW_IF) // cppcheck-suppress syntaxError
{
try
{
THROW_IF(false, type::InvalidArgument, message);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::InvalidArgument);
ASSERT_EQ(ex.what(), what);
}
}
TEST_F(InvalidArgument, macro_THROW)
{
try
{
THROW(type::InvalidArgument, message);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::InvalidArgument);
// NOTE: no case for ex.what() because message is formatted within macro
}
}
TEST_F(InvalidArgument, rethrow)
{
ASSERT_THROW(
{
try
{
throw type::InvalidArgument(what);
}
catch (const type::Exception& ex)
{
ASSERT_EQ(ex.type(), t_type::InvalidArgument);
ASSERT_EQ(ex.what(), what);
throw;
}
},
type::Exception);
}
TEST_F(InvalidArgument, copy_assignment)
{
type::InvalidArgument one(what);
type::InvalidArgument two;
two = one;
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(InvalidArgument, copy_ctor)
{
type::InvalidArgument one(what);
type::InvalidArgument two(one);
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(InvalidArgument, move_assignment)
{
type::InvalidArgument one(what);
type::InvalidArgument two;
two = std::move(one);
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
TEST_F(InvalidArgument, move_ctor)
{
type::InvalidArgument one(what);
type::InvalidArgument two(std::move(one));
ASSERT_EQ(one.type(), two.type());
ASSERT_EQ(one.what(), two.what());
}
} // namespace unit
} // namespace tests
} // namespace docker_finance
#endif // CONTAINER_SRC_ROOT_TEST_UNIT_TYPE_HH_
// # vim: sw=2 sts=2 si ai et