1
- #include < type_traits>
2
1
#include < memory>
3
- namespace csari
4
- {
2
+ #include < stdexcept>
3
+ #include < type_traits>
4
+
5
+ namespace csari {
5
6
template <class Sig >
6
7
struct flexInvokable ;
7
-
8
- namespace flexInvokableInternal
9
- {
10
- template <class T >
11
- struct emplace_as
12
- {
13
- };
14
- } // namespace flexInvokableInternal
15
8
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:
18
23
// can be default ctored and moved:
19
24
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 ;
22
30
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)} {}
38
35
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:
45
37
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;
79
57
}
80
58
}
81
59
82
- // empty the fire_once:
83
- void clear ()
84
- {
60
+ void reset () {
85
61
invoke = nullptr ;
86
62
ptr.reset ();
87
63
}
88
64
89
65
// test if it is non-empty:
90
66
explicit operator bool () const { return (bool )ptr; }
91
67
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 ;
110
72
};
111
- } // namespace csari
73
+ } // namespace csari
0 commit comments