FE 0.10.2
Header-only C++ frontend library
Loading...
Searching...
No Matches
cast.h
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <format>
5#include <utility>
6
7#include <fe/assert.h>
8
9namespace fe {
10
11template<class T>
12concept Nodeable = requires(T n) {
13 T::Node;
14 n.node();
15};
16
17/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
18/// for some nice `dynamic_cast`-style wrappers.
19template<class B>
21public:
22 // clang-format off
23 /// `static_cast` with debug check.
24 template<class T> T* as() { assert(isa<T>()); return static_cast<T*>(this); }
25
26 /// `dynamic_cast`.
27 /// If @p T isa fe::Nodeable, it will use `node()`, otherwise a `dynamic_cast`.
28 template<class T>
29 T* isa() {
30 if constexpr (Nodeable<T>) {
31 return static_cast<B*>(this)->node() == T::Node ? static_cast<T*>(this) : nullptr;
32 } else {
33 return dynamic_cast<T*>(static_cast<B*>(this));
34 }
35 }
36
37 template<class T> const T* as() const { return const_cast<RuntimeCast*>(this)->template as<T >(); } ///< `const` version.
38 template<class T> const T* isa() const { return const_cast<RuntimeCast*>(this)->template isa<T >(); } ///< `const` version.
39 // clang-format on
40
41 /// Like as() but - instead of merely asserting via isa() in `Debug` builds - throws a `std::logic_error` with a
42 /// formatted message when the cast fails.
43 /// @p fmt / @p args describe what was expected: a plain string works, as does a format string plus arguments.
44 /// If `B` is `std::formattable`, the offending object is appended to the message.
45 template<class T, class... Args>
46 T* expect(std::format_string<Args...> fmt, Args&&... args) {
47 if (auto res = isa<T>()) return res;
48 auto what = std::format(fmt, std::forward<Args>(args)...);
49 if constexpr (std::formattable<const B*, char>)
50 throwf("expected {}, but got '{}'", what, static_cast<const B*>(this));
51 else
52 throwf("expected {}", what);
53 }
54
55 /// `const` version.
56 template<class T, class... Args>
57 const T* expect(std::format_string<Args...> fmt, Args&&... args) const {
58 return const_cast<RuntimeCast*>(this)->template expect<T>(fmt, std::forward<Args>(args)...);
59 }
60};
61
62} // namespace fe
Inherit from this class using CRTP, for some nice dynamic_cast-style wrappers.
Definition cast.h:20
T * as()
static_cast with debug check.
Definition cast.h:24
T * isa()
dynamic_cast.
Definition cast.h:29
const T * expect(std::format_string< Args... > fmt, Args &&... args) const
const version.
Definition cast.h:57
const T * isa() const
const version.
Definition cast.h:38
T * expect(std::format_string< Args... > fmt, Args &&... args)
Like as() but - instead of merely asserting via isa() in Debug builds - throws a std::logic_error wit...
Definition cast.h:46
const T * as() const
const version.
Definition cast.h:37
Definition arena.h:13
void throwf(std::format_string< Args... > fmt, Args &&... args)
Throws a T (a std::logic_error by default) whose message is std::format(fmt, args....
Definition assert.h:14