Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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