Skip to content

Commit fc42765

Browse files
authored
Merge pull request #40 from iris-cpp/add-throwf
Add `throwf`
2 parents 7687ea3 + ff92f74 commit fc42765

5 files changed

Lines changed: 163 additions & 1 deletion

File tree

cpp.hint

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717

1818
#define IRIS_FORCEINLINE
1919
#define IRIS_LIFETIMEBOUND
20+
21+
#define IRIS_THROW_NORETURN [[noreturn]]
22+

include/iris/exception.hpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#ifndef IRIS_EXCEPTION_HPP
2+
#define IRIS_EXCEPTION_HPP
3+
4+
#include <iris/string.hpp>
5+
6+
#include <exception>
7+
#include <format>
8+
#include <stdexcept>
9+
#include <string>
10+
#include <string_view>
11+
#include <type_traits>
12+
#include <utility>
13+
14+
#ifndef IRIS_THROW_IMPL
15+
# define IRIS_THROW_IMPL(...) throw __VA_ARGS__
16+
#endif
17+
18+
#ifndef IRIS_THROW_NORETURN
19+
# define IRIS_THROW_NORETURN [[noreturn]]
20+
#endif
21+
22+
namespace iris {
23+
24+
namespace detail {
25+
26+
template<class T, class... Args>
27+
concept constructible_from_string_like_types =
28+
std::is_constructible_v<T, Args..., std::string> ||
29+
std::is_constructible_v<T, Args..., std::string_view> ||
30+
std::is_constructible_v<T, Args..., const char*>;
31+
32+
} // detail
33+
34+
inline namespace error_functions {
35+
36+
template<class E>
37+
IRIS_THROW_NORETURN void throwf()
38+
{
39+
static_assert(std::is_base_of_v<std::exception, E>);
40+
static_assert(std::is_constructible_v<E>);
41+
IRIS_THROW_IMPL(E{});
42+
}
43+
44+
template<class E, class Arg, class... Rest>
45+
requires std::is_constructible_v<E, Arg, Rest...>
46+
IRIS_THROW_NORETURN void throwf(Arg&& arg, Rest&&... rest)
47+
{
48+
static_assert(std::is_base_of_v<std::exception, E>);
49+
static_assert(!std::is_base_of_v<std::exception, std::remove_cvref_t<Arg>>, "don't copy/move construct exception types directly");
50+
IRIS_THROW_IMPL(E{std::forward<Arg>(arg), std::forward<Rest>(rest)...});
51+
}
52+
53+
template<class E, class... Args>
54+
requires detail::constructible_from_string_like_types<E>
55+
IRIS_THROW_NORETURN void throwf(std::format_string<Args...> fmt, Args&&... args)
56+
{
57+
static_assert(std::is_base_of_v<std::exception, E>);
58+
IRIS_THROW_IMPL(E{std::format(std::move(fmt), std::forward<Args>(args)...)});
59+
}
60+
61+
template<class E, NotStringLike Arg0, class... Args>
62+
requires detail::constructible_from_string_like_types<E, Arg0>
63+
IRIS_THROW_NORETURN void throwf(Arg0&& arg0, std::format_string<Args...> fmt, Args&&... args)
64+
{
65+
static_assert(std::is_base_of_v<std::exception, E>);
66+
IRIS_THROW_IMPL(E{std::forward<Arg0>(arg0), std::format(std::move(fmt), std::forward<Args>(args)...)});
67+
}
68+
69+
template<class E, NotStringLike Arg0, NotStringLike Arg1, class... Args>
70+
requires detail::constructible_from_string_like_types<E, Arg0, Arg1>
71+
IRIS_THROW_NORETURN void throwf(Arg0&& arg0, Arg1&& arg1, std::format_string<Args...> fmt, Args&&... args)
72+
{
73+
static_assert(std::is_base_of_v<std::exception, E>);
74+
IRIS_THROW_IMPL(
75+
E{
76+
std::forward<Arg0>(arg0), std::forward<Arg1>(arg1),
77+
std::format(std::move(fmt), std::forward<Args>(args)...)
78+
}
79+
);
80+
}
81+
82+
} // error_functions
83+
84+
} // iris
85+
86+
#endif

include/iris/string.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ concept StringLike = requires(T t) {
1313
std::basic_string_view{t};
1414
};
1515

16+
template<class T>
17+
concept NotStringLike = !StringLike<T>;
18+
1619
} // iris
1720

1821
#endif

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ endfunction()
150150
# Iris tests
151151

152152
if(PROJECT_IS_TOP_LEVEL)
153-
if(IRIS_CI_COMPONENT STREQUAL iris)
153+
if(NOT DEFINED IRIS_CI_COMPONENT OR IRIS_CI_COMPONENT STREQUAL iris)
154154
add_subdirectory(rvariant)
155155

156156
set(

test/core.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <iris/requirements.hpp>
77
#include <iris/compare.hpp>
88
#include <iris/fixed_string.hpp>
9+
#include <iris/exception.hpp>
910

1011
#include <concepts>
1112
#include <utility>
@@ -452,4 +453,73 @@ TEST_CASE("fixed_string")
452453
CHECK((std::ranges::equal(str, "foobar"sv)));
453454
}
454455

456+
457+
#define IRIS_TEST_THROWF(expected, E, ...) \
458+
CHECK_THROWS_AS( \
459+
([]{ \
460+
try { \
461+
iris::throwf<E>(__VA_ARGS__); \
462+
} catch (std::exception const& e) { \
463+
CHECK(e.what() == std::string_view{expected}); \
464+
throw; \
465+
} \
466+
})(), \
467+
E \
468+
)
469+
470+
TEST_CASE("throwf")
471+
{
472+
class my_exception : public std::runtime_error
473+
{
474+
public:
475+
my_exception(std::string const& name, std::string const& message)
476+
: runtime_error(name + ": " + message)
477+
{}
478+
};
479+
480+
IRIS_TEST_THROWF("foo", std::runtime_error, "foo");
481+
IRIS_TEST_THROWF("foo", std::runtime_error, std::string{"foo"});
482+
IRIS_TEST_THROWF("foo", std::runtime_error, std::string{"foo"}.c_str());
483+
IRIS_TEST_THROWF("foo", std::runtime_error, std::string_view{"foo"});
484+
IRIS_TEST_THROWF("{}", std::runtime_error, "{}");
485+
IRIS_TEST_THROWF("42", std::runtime_error, "{}", 42);
486+
IRIS_TEST_THROWF("foo", std::runtime_error, "{}", "foo");
487+
IRIS_TEST_THROWF("foo", std::runtime_error, "{}", std::string{"foo"});
488+
IRIS_TEST_THROWF("foo", std::runtime_error, "{}", std::string{"foo"}.c_str());
489+
IRIS_TEST_THROWF("foo", std::runtime_error, "{}", std::string_view{"foo"});
490+
491+
IRIS_TEST_THROWF("{}: bar", my_exception, "{}", "bar");
492+
IRIS_TEST_THROWF("foo: bar", my_exception, "foo", "bar");
493+
494+
// constructible with argument
495+
IRIS_TEST_THROWF("foobar", std::runtime_error, "foobar");
496+
497+
// constructible with format string
498+
IRIS_TEST_THROWF("33 - 4", std::runtime_error, "{} - {}", 33, 4);
499+
500+
CHECK_THROWS_AS(
501+
([]{
502+
try {
503+
iris::throwf<std::system_error>(std::make_error_code(std::errc::invalid_argument), "{}", 42);
504+
} catch (std::system_error const& e) {
505+
CHECK(e.code() == std::make_error_code(std::errc::invalid_argument));
506+
throw;
507+
}
508+
})(),
509+
std::system_error
510+
);
511+
512+
CHECK_THROWS_AS(
513+
([]{
514+
try {
515+
iris::throwf<std::system_error>(33 - 4, std::generic_category(), "{}", 42);
516+
} catch (std::system_error const& e) {
517+
CHECK((e.code() == std::error_code{33 - 4, std::generic_category()}));
518+
throw;
519+
}
520+
})(),
521+
std::system_error
522+
);
523+
}
524+
455525
} // unit_test

0 commit comments

Comments
 (0)