Skip to content

Commit 9702670

Browse files
authored
Protect against uninitialized services (#666)
* 🎨 Use injected class name in `nexus` * 🦺 Protect against uninitialized services Problem: - If services are uninitialized, either through not being exported properly, or through `nexus.init()` not being called, the service pointers are null and the resulting UB causes hard-to-diagnose effects. Solution: - Initialize services to functions that cause a runtime panic.
1 parent 01659ce commit 9702670

18 files changed

+256
-14
lines changed

include/cib/builder_meta.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#pragma once
22

3+
#include <concepts>
4+
35
namespace cib {
46
template <typename T>
57
concept builder_meta = requires {
68
typename T::builder_t;
79
typename T::interface_t;
10+
{ T::uninitialized() } -> std::same_as<typename T::interface_t>;
811
};
912

1013
template <builder_meta T> using builder_t = typename T::builder_t;

include/cib/built.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
#include <cib/builder_meta.hpp>
44

55
namespace cib {
6-
template <builder_meta ServiceMeta> interface_t<ServiceMeta> service;
6+
template <builder_meta ServiceMeta>
7+
constinit auto service = ServiceMeta::uninitialized();
78
} // namespace cib

include/cib/callback.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <cib/builder_meta.hpp>
44

55
#include <stdx/compiler.hpp>
6+
#include <stdx/panic.hpp>
67

78
#include <array>
89
#include <concepts>
@@ -119,5 +120,12 @@ template <int NumFuncs = 0, typename... ArgTypes> struct builder {
119120
template <typename... ArgTypes> struct service {
120121
using builder_t = builder<0, ArgTypes...>;
121122
using interface_t = void (*)(ArgTypes...);
123+
124+
CONSTEVAL static auto uninitialized() -> interface_t {
125+
return [](ArgTypes...) {
126+
stdx::panic<
127+
"Attempting to run callback before it is initialized">();
128+
};
129+
}
122130
};
123131
} // namespace callback

include/cib/nexus.hpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,22 @@ namespace cib {
1616
*
1717
* @see cib::config
1818
*/
19-
template <typename Config> struct nexus {
20-
private:
21-
using this_t = nexus<Config>;
2219

20+
template <typename Config> struct nexus {
2321
// Workaround unfortunate bug in clang where it can't deduce "auto" sometimes
2422
#define CIB_BUILD_SERVICE \
2523
initialized<Config, Tag>::value.template build<initialized<Config, Tag>>()
2624

27-
public:
2825
template <typename Tag>
2926
constexpr static decltype(CIB_BUILD_SERVICE) service = CIB_BUILD_SERVICE;
3027
#undef CIB_BUILD_SERVICE
3128

3229
static void init() {
3330
auto const service = []<typename T> {
34-
using from_t = std::remove_cvref_t<decltype(this_t::service<T>)>;
31+
using from_t = std::remove_cvref_t<decltype(nexus::service<T>)>;
3532
using to_t = std::remove_cvref_t<decltype(cib::service<T>)>;
3633

37-
auto &service_impl = this_t::service<T>;
34+
auto &service_impl = nexus::service<T>;
3835
if constexpr (std::is_convertible_v<from_t, to_t>) {
3936
cib::service<T> = service_impl;
4037
} else {

include/flow/builder.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#include <flow/graph_builder.hpp>
55
#include <flow/impl.hpp>
66

7+
#include <stdx/compiler.hpp>
78
#include <stdx/ct_string.hpp>
9+
#include <stdx/panic.hpp>
810

911
namespace flow {
1012
template <stdx::ct_string Name = "">
@@ -13,5 +15,13 @@ using builder = graph<Name, graph_builder<Name, impl>>;
1315
template <stdx::ct_string Name = ""> struct service {
1416
using builder_t = builder<Name>;
1517
using interface_t = FunctionPtr;
18+
19+
CONSTEVAL static auto uninitialized() -> interface_t {
20+
return [] {
21+
using namespace stdx::literals;
22+
stdx::panic<"Attempting to run flow ("_cts + Name +
23+
") before it is initialized"_cts>();
24+
};
25+
}
1626
};
1727
} // namespace flow

include/msg/handler_interface.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#pragma once
22

3+
#include <stdx/compiler.hpp>
4+
#include <stdx/ct_conversions.hpp>
5+
#include <stdx/ct_string.hpp>
6+
#include <stdx/panic.hpp>
7+
#include <stdx/type_traits.hpp>
8+
39
namespace msg {
410
template <typename MsgBase, typename... ExtraCallbackArgs>
511
struct handler_interface {
@@ -8,4 +14,33 @@ struct handler_interface {
814
virtual auto handle(MsgBase const &msg,
915
ExtraCallbackArgs... extra_args) const -> bool = 0;
1016
};
17+
18+
namespace detail {
19+
template <typename M>
20+
concept named_msg_base =
21+
stdx::is_specialization_of<decltype(M::name), stdx::ct_string>().value;
22+
23+
template <typename M> CONSTEVAL auto name_for_msg() {
24+
if constexpr (detail::named_msg_base<M>) {
25+
return M::name;
26+
} else {
27+
constexpr auto name = stdx::type_as_string<M>();
28+
return stdx::ct_string<name.size() + 1>{name};
29+
}
30+
}
31+
} // namespace detail
32+
33+
template <typename MsgBase, typename... ExtraCallbackArgs>
34+
struct uninitialized_handler_t
35+
: handler_interface<MsgBase, ExtraCallbackArgs...> {
36+
auto is_match(MsgBase const &) const -> bool override { return false; }
37+
38+
auto handle(MsgBase const &, ExtraCallbackArgs...) const -> bool override {
39+
using namespace stdx::literals;
40+
stdx::panic<"Attempting to handle msg ("_cts +
41+
detail::name_for_msg<MsgBase>() +
42+
") before service is initialized"_cts>();
43+
return false;
44+
}
45+
};
1146
} // namespace msg

include/msg/indexed_service.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <msg/handler_interface.hpp>
44
#include <msg/indexed_builder.hpp>
55

6+
#include <stdx/compiler.hpp>
67
#include <stdx/tuple.hpp>
78

89
namespace msg {
@@ -12,5 +13,11 @@ struct indexed_service {
1213
ExtraCallbackArgs...>;
1314
using interface_t =
1415
handler_interface<MsgBase, ExtraCallbackArgs...> const *;
16+
17+
constexpr static auto uninitialized_v =
18+
uninitialized_handler_t<MsgBase, ExtraCallbackArgs...>{};
19+
CONSTEVAL static auto uninitialized() -> interface_t {
20+
return &uninitialized_v;
21+
}
1522
};
1623
} // namespace msg

include/msg/message.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ template <stdx::ct_string Name, typename... Fields> struct message {
349349
(... and Fields::template fits_inside<S>());
350350

351351
template <typename T> struct base {
352+
constexpr static auto name = Name;
353+
352354
constexpr auto as_derived() const -> T const & {
353355
return static_cast<T const &>(*this);
354356
}

include/msg/service.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@
33
#include <msg/handler_builder.hpp>
44
#include <msg/handler_interface.hpp>
55

6-
#include <stdx/tuple.hpp>
6+
#include <stdx/compiler.hpp>
77

88
namespace msg {
99
template <typename MsgBase, typename... ExtraCallbackArgs> struct service {
1010
using builder_t =
1111
handler_builder<stdx::tuple<>, MsgBase, ExtraCallbackArgs...>;
1212
using interface_t =
1313
handler_interface<MsgBase, ExtraCallbackArgs...> const *;
14+
15+
constexpr static auto uninitialized_v =
16+
uninitialized_handler_t<MsgBase, ExtraCallbackArgs...>{};
17+
CONSTEVAL static auto uninitialized() -> interface_t {
18+
return &uninitialized_v;
19+
}
1420
};
1521
} // namespace msg

test/cib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_tests(
22
FILES
33
builder_meta
44
callback
5+
callback_uninit
56
nexus
67
readme_hello_world
78
LIBRARIES

0 commit comments

Comments
 (0)