FakeVirtual.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <type_traits>
7 #include <typeinfo>
8 
10 #include "Utilities/PrettyType.hpp"
11 #include "Utilities/Requires.hpp"
12 #include "Utilities/TMPL.hpp"
13 #include "Utilities/TypeTraits.hpp"
14 
15 /// \ingroup UtilitiesGroup
16 /// \brief Define a function that acts similarly to a virtual
17 /// function, but can take template parameters.
18 ///
19 /// \details `DEFINE_FAKE_VIRTUAL(func)` defines the function
20 /// `fake_virtual_func` and the struct `FakeVirtualInherit_func`. It
21 /// should usually be called in a detail namespace.
22 ///
23 /// A base class `Base` using this functionality should define a type
24 /// \code
25 /// using Inherit = FakeVirtualInherit_func<Base>;
26 /// \endcode
27 /// and a member function `func` wrapping `fake_virtual_func`, with
28 /// the wrapper passing the derived classes as a typelist as the first
29 /// template argument and the `this` pointer as the first normal
30 /// argument.
31 ///
32 /// Derived classes should then inherit from `Base::Inherit` instead
33 /// of directly from `Base`. (`Base::Inherit` inherits from `Base`.)
34 ///
35 /// If the base class has no pure virtual functions remaining it will
36 /// generally be desirable to mark the constructors and assignment
37 /// operators protected so that a bare base class cannot be instantiated.
38 ///
39 /// If it is necessary to use multiple fake virtual functions with the
40 /// same base class, the `Inherit` definition can nest the fake
41 /// virtual classes:
42 /// \code
43 /// using Inherit = FakeVirtualInherit_func1<FakeVirtualInherit_func2<Base>>;
44 /// \endcode
45 ///
46 /// \example
47 /// \snippet Test_FakeVirtual.cpp fake_virtual_example
48 ///
49 /// \see call_with_dynamic_type
50 #define DEFINE_FAKE_VIRTUAL(function) \
51  /* This struct is only needed for producing an error if the function */ \
52  /* is not overridden in the derived class. */ \
53  template <typename Base> \
54  struct FakeVirtualInherit_##function : public Base { \
55  using Base::Base; \
56  /* clang-tidy: I think "= delete" was overlooked in the guideline */ \
57  void function(...) const = delete; /* NOLINT */ \
58  }; \
59  \
60  template <typename Classes, typename... TArgs, typename Base, \
61  typename... Args> \
62  decltype(auto) fake_virtual_##function(Base* obj, Args&&... args) noexcept { \
63  /* clang-tidy: macro arg in parentheses */ \
64  return call_with_dynamic_type< \
65  decltype(obj->template function<TArgs...>(args...)), /* NOLINT */ \
66  Classes>( \
67  obj, [&args...](auto* const dynamic_obj) noexcept -> decltype(auto) { \
68  static_assert( \
69  cpp17::is_base_of_v<typename Base::Inherit, \
70  std::decay_t<decltype(*dynamic_obj)>>, \
71  "Derived class does not inherit from Base::Inherit"); \
72  /* clang-tidy: macro arg in parentheses */ \
73  return dynamic_obj->template function<TArgs...>(/* NOLINT */ \
74  std::forward<Args>( \
75  args)...); \
76  }); \
77  }
78 
79 /// \cond
80 template <typename Result, typename Classes, typename Base, typename Callable,
81  Requires<(tmpl::size<Classes>::value == 0)> = nullptr>
82 [[noreturn]] Result call_with_dynamic_type(Base* const obj,
83  Callable&& /*f*/) noexcept {
85  << " is not registered with "
87 }
88 /// \endcond
89 
90 /// \ingroup UtilitiesGroup
91 /// \brief Call a functor with the derived type of a base class pointer.
92 ///
93 /// \details Calls functor with obj cast to type `T*` where T is the
94 /// dynamic type of `*obj`. The decay type of `T` must be in the
95 /// provided list of classes.
96 ///
97 /// \see DEFINE_FAKE_VIRTUAL
98 ///
99 /// \tparam Result the return type
100 /// \tparam Classes the typelist of derived classes
101 template <typename Result, typename Classes, typename Base, typename Callable,
102  Requires<(tmpl::size<Classes>::value != 0)> = nullptr>
103 Result call_with_dynamic_type(Base* const obj, Callable&& f) noexcept {
104  using Derived = tmpl::front<Classes>;
105  using DerivedPointer =
106  std::conditional_t<std::is_const<Base>::value, Derived const*, Derived*>;
107  // If we want to allow creatable classses to return objects of
108  // types derived from themselves then this will have to be changed
109  // to a dynamic_cast, but we probably won't want that and this
110  // form is significantly faster.
111  return typeid(*obj) == typeid(Derived)
112  ? std::forward<Callable>(f)(static_cast<DerivedPointer>(obj))
113  : call_with_dynamic_type<Result, tmpl::pop_front<Classes>>(
114  obj, std::forward<Callable>(f));
115 }
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:35
Result call_with_dynamic_type(Base *const obj, Callable &&f) noexcept
Call a functor with the derived type of a base class pointer.
Definition: FakeVirtual.hpp:103
std::string get_name()
Returns a string with the prettiest typename known for the type T.
Definition: PrettyType.hpp:674
Defines the type alias Requires.
std::string get_runtime_type_name(const T &x)
Returns a string with the prettiest typename known for the runtime type of x.
Definition: PrettyType.hpp:688
Contains a pretty_type library to write types in a "pretty" format.
Wraps the template metaprogramming library used (brigand)
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t ...
Definition: Requires.hpp:67
Defines macro ERROR.
Defines type traits, some of which are future STL type_traits header.