Skip to content

Commit 4387f30

Browse files
authored
Merge pull request #2 from CihanSari/feature-refactor
Remove internal namespace. Replace the template specializations with …
2 parents a45a3f6 + 54d78e3 commit 4387f30

File tree

2 files changed

+71
-91
lines changed

2 files changed

+71
-91
lines changed
+53-91
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,73 @@
1-
#include <type_traits>
21
#include <memory>
3-
namespace csari
4-
{
2+
#include <stdexcept>
3+
#include <type_traits>
4+
5+
namespace csari {
56
template <class Sig>
67
struct flexInvokable;
7-
8-
namespace flexInvokableInternal
9-
{
10-
template <class T>
11-
struct emplace_as
12-
{
13-
};
14-
} // namespace flexInvokableInternal
158
template <class R, class... Args>
16-
struct flexInvokable<R(Args...)>
17-
{
9+
struct flexInvokable<R(Args...)> final {
10+
private:
11+
template <class F>
12+
struct parser {
13+
using D = F;
14+
static auto const IsConvertible =
15+
std::is_convertible<std::invoke_result_t<F, Args...>, R>::value;
16+
static auto const IsVoid = std::is_same_v<R, void>;
17+
static auto const IsSelf = std::is_same_v<D, flexInvokable>;
18+
static auto const IsValidFunction = IsConvertible || IsVoid || !IsSelf;
19+
using EnableIfValid = std::enable_if_t<IsValidFunction, bool>;
20+
};
21+
22+
public:
1823
// can be default ctored and moved:
1924
flexInvokable() = default;
20-
flexInvokable(flexInvokable &&) = default;
21-
flexInvokable &operator=(flexInvokable &&) = default;
25+
flexInvokable(flexInvokable &&) noexcept = default;
26+
flexInvokable &operator=(flexInvokable &&) noexcept = default;
27+
flexInvokable(flexInvokable const &) = delete;
28+
flexInvokable &operator=(flexInvokable const &) = delete;
29+
~flexInvokable() = default;
2230

23-
// implicitly create from a type that can be compatibly invoked
24-
// and isn't a fire_once itself
25-
template <class F,
26-
std::enable_if_t<!std::is_same<std::decay_t<F>, flexInvokable>{},
27-
int> = 0,
28-
std::enable_if_t<std::is_convertible<
29-
#if _HAS_CXX17
30-
std::invoke_result_t<F, Args...>
31-
#else
32-
std::result_of_t<std::decay_t<F> &(Args...)>
33-
#endif
34-
,
35-
R>{} ||
36-
std::is_same<R, void>{},
37-
int> = 0>
31+
// implicitly create from a type that can be invoked
32+
template <class F, class P = parser<std::decay_t<F>>,
33+
typename P::EnableIfValid = true>
34+
flexInvokable(F &&f) : flexInvokable{P{}, std::forward<F>(f)} {}
3835

39-
flexInvokable(F &&f)
40-
: flexInvokable(flexInvokableInternal::emplace_as<std::decay_t<F>>{},
41-
std::forward<F>(f))
42-
{
43-
}
44-
// emplacement construct using the emplace_as tag type:
36+
// Construct using the parser tag type:
4537
template <class F, class... FArgs>
46-
flexInvokable(flexInvokableInternal::emplace_as<F>, FArgs &&... fargs)
47-
{
48-
rebind<F>(std::forward<FArgs>(fargs)...);
49-
}
50-
// invoke in the case where R is not void:
51-
template <class R2 = R, std::enable_if_t<!std::is_same<R2, void>{}, int> = 0>
52-
R2 operator()(Args... args) &&
53-
{
54-
try
55-
{
56-
R2 ret = invoke(ptr.get(), std::forward<Args>(args)...);
57-
clear();
58-
return ret;
59-
}
60-
catch (std::runtime_error e)
61-
{
62-
clear();
63-
throw;
64-
}
65-
}
66-
// invoke in the case where R is void:
67-
template <class R2 = R, std::enable_if_t<std::is_same<R2, void>{}, int> = 0>
68-
R2 operator()(Args... args) &&
69-
{
70-
try
71-
{
72-
invoke(ptr.get(), std::forward<Args>(args)...);
73-
clear();
74-
}
75-
catch (...)
76-
{
77-
clear();
78-
throw;
38+
flexInvokable(parser<F>, FArgs &&...args)
39+
: ptr{std::make_shared<F>(std::forward<FArgs>(args)...)},
40+
invoke{[](void *pf, Args... args) -> R {
41+
return (*(F *)pf)(std::forward<Args>(args)...);
42+
}} {}
43+
44+
auto operator()(Args... args) -> decltype(auto) {
45+
try {
46+
if constexpr (std::is_same_v<R, void>) {
47+
invoke(ptr.get(), std::forward<Args>(args)...);
48+
reset();
49+
} else {
50+
auto ret = invoke(ptr.get(), std::forward<Args>(args)...);
51+
reset();
52+
return ret;
53+
}
54+
} catch (std::exception const &e) {
55+
reset();
56+
throw e;
7957
}
8058
}
8159

82-
// empty the fire_once:
83-
void clear()
84-
{
60+
void reset() {
8561
invoke = nullptr;
8662
ptr.reset();
8763
}
8864

8965
// test if it is non-empty:
9066
explicit operator bool() const { return (bool)ptr; }
9167

92-
// change what the flexInvokable contains:
93-
template <class F, class... FArgs>
94-
void rebind(FArgs &&... fargs)
95-
{
96-
clear();
97-
auto pf = std::make_unique<F>(std::forward<FArgs>(fargs)...);
98-
invoke = [](void *pf, Args... args) -> R {
99-
return (*(F *)pf)(std::forward<Args>(args)...);
100-
};
101-
ptr = {pf.release(), [](void *pf) { delete (F *)(pf); }};
102-
}
103-
104-
private:
105-
// storage. A unique pointer with deleter
106-
// and an invoker function pointer:
107-
std::unique_ptr<void, void (*)(void *)> ptr{nullptr, [](void *) {}};
108-
R (*invoke)
109-
(void *, Args...) = nullptr;
68+
private:
69+
// storage. shared_ptr to type-erase and clean-up.
70+
std::shared_ptr<void> ptr{};
71+
R (*invoke)(void *, Args...) = nullptr;
11072
};
111-
} // namespace csari
73+
} // namespace csari

test/src/unit.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,23 @@ TEST_CASE("ArgumentTests") {
5757
REQUIRE(std::move(*it)(6));
5858
}
5959

60+
TEST_CASE("ThrowTests") {
61+
// void()
62+
struct fiveThrow : std::runtime_error {
63+
using runtime_error::runtime_error;
64+
};
65+
auto throw1 = [mu = std::make_unique<std::string>("five")] {
66+
throw fiveThrow{*mu};
67+
};
68+
struct sixThrow : std::runtime_error {
69+
using runtime_error::runtime_error;
70+
};
71+
auto throw2 = [mu = std::make_unique<std::string>("six")] {
72+
throw sixThrow{*mu};
73+
};
74+
REQUIRE_THROWS_AS(throw1(), fiveThrow);
75+
REQUIRE_THROWS_AS(throw2(), sixThrow);
76+
}
77+
6078
int examples();
6179
TEST_CASE("Examples") { REQUIRE(examples() == 0); }

0 commit comments

Comments
 (0)