Skip to content

Commit

Permalink
Updated cms::Exception using modern C++
Browse files Browse the repository at this point in the history
- Added format option
- Use concepts to constrain operator<<
  • Loading branch information
Dr15Jones committed Aug 19, 2024
1 parent 1d7ff69 commit 8c13810
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 17 deletions.
44 changes: 27 additions & 17 deletions FWCore/Utilities/interface/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,17 @@
#include <string>
#include <exception>
#include <type_traits>
#include <string_view>
#include <concepts>

#include "fmt/format.h"

#include "FWCore/Utilities/interface/thread_safety_macros.h"
#include "FWCore/Utilities/interface/Likely.h"
#include "FWCore/Utilities/interface/Visibility.h"

namespace cms {

class Exception;
namespace detail {
// Used for SFINAE to control the instantiation of the stream insertion
// member template needed to support streaming output to an object
// of type cms::Exception, or a subclass of cms::Exception.
template <typename E>
using exception_type =
std::enable_if_t<std::is_base_of_v<Exception, std::remove_reference_t<E>>, std::remove_reference_t<E>>;

} // namespace detail

class dso_export Exception : public std::exception {
public:
explicit Exception(std::string const& aCategory);
Expand Down Expand Up @@ -118,13 +111,20 @@ namespace cms {
//

template <typename E, typename T>
friend typename detail::exception_type<E>& operator<<(E&& e, T const& stuff);
requires std::derived_from<std::remove_reference_t<E>, Exception>
friend E& operator<<(E&& e, T const& stuff);

template <typename E>
friend typename detail::exception_type<E>& operator<<(E&& e, std::ostream& (*f)(std::ostream&));
requires std::derived_from<std::remove_reference_t<E>, Exception>
friend E& operator<<(E&& e, std::ostream& (*f)(std::ostream&));

template <typename E>
friend typename detail::exception_type<E>& operator<<(E&& e, std::ios_base& (*f)(std::ios_base&));
requires std::derived_from<std::remove_reference_t<E>, Exception>
friend E& operator<<(E&& e, std::ios_base& (*f)(std::ios_base&));

template <typename... Args>
inline void format(fmt::format_string<Args...> format, Args&&... args);
inline void vformat(std::string_view fmt, fmt::format_args args);

// This function is deprecated and we are in the process of removing
// all code that uses it from CMSSW. It will then be deleted.
Expand Down Expand Up @@ -152,20 +152,30 @@ namespace cms {

// -------- implementation ---------

template <typename... Args>
inline void Exception::format(fmt::format_string<Args...> format, Args&&... args) {
ost_ << fmt::format(std::move(format), std::forward<Args>(args)...);
}

inline void Exception::vformat(std::string_view format, fmt::format_args args) { ost_ << fmt::vformat(format, args); }

template <typename E, typename T>
inline typename detail::exception_type<E>& operator<<(E&& e, T const& stuff) {
requires std::derived_from<std::remove_reference_t<E>, Exception>
inline E& operator<<(E&& e, T const& stuff) {
e.ost_ << stuff;
return e;
}

template <typename E>
inline typename detail::exception_type<E>& operator<<(E&& e, std::ostream& (*f)(std::ostream&)) {
requires std::derived_from<std::remove_reference_t<E>, Exception>
inline E& operator<<(E&& e, std::ostream& (*f)(std::ostream&)) {
f(e.ost_);
return e;
}

template <typename E>
inline typename detail::exception_type<E>& operator<<(E&& e, std::ios_base& (*f)(std::ios_base&)) {
requires std::derived_from<std::remove_reference_t<E>, Exception>
inline E& operator<<(E&& e, std::ios_base& (*f)(std::ios_base&)) {
f(e.ost_);
return e;
}
Expand Down
41 changes: 41 additions & 0 deletions FWCore/Utilities/test/test_catch2_Exception.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ namespace {
"\n"
"Gave up\n";

constexpr char expected_func4[] =
"An exception of category 'DataCorrupt' occurred.\n"
"Exception Message:\n"
"This is just a test: \n"
"double: 1.11111\n"
"float: 2.22222\n"
"uint: 75\n"
"string: a string\n"
"char*: a nonconst pointer\n"
"char[]: a c-style array\n";

void func3() {
double d = 1.11111;
float f = 2.22222;
Expand Down Expand Up @@ -77,10 +88,40 @@ namespace {
}
}

void func4() {
double d = 1.11111;
float f = 2.22222;
unsigned int i = 75U;
std::string s("a string");
char* c1 = const_cast<char*>("a nonconst pointer");
char c2[] = "a c-style array";
Thing thing(4);

// throw cms::Exception("DataCorrupt")
cms::Exception e("DataCorrupt");
e.format(
"This is just a test: \n"
"double: {}\n"
"float: {}\n"
"uint: {}\n"
"string: {}\n"
"char*: {}\n"
"char[]: {}\n",
d,
f,
i,
s,
c1,
c2);

throw e;
}

} // namespace

TEST_CASE("Test cms::Exception", "[cms::Exception]") {
SECTION("throw") { REQUIRE_THROWS_WITH(func1(), expected); }
SECTION("throw with format") { REQUIRE_THROWS_WITH(func4(), expected_func4); }
SECTION("returnCode") {
cms::Exception e1("ABC");
REQUIRE(e1.returnCode() == 8001);
Expand Down

0 comments on commit 8c13810

Please sign in to comment.