mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-27 07:02:11 +00:00
164 lines
4.9 KiB
Plaintext
164 lines
4.9 KiB
Plaintext
|
|
## Custom RTTI
|
|
|
|
Stock policies use the `std_rtti` implementation of `rtti`. Here is its full
|
|
source:
|
|
|
|
[source,c++]
|
|
----
|
|
struct std_rtti : rtti {
|
|
template<typename T>
|
|
static type_id static_type() {
|
|
return reinterpret_cast<type_id>(&typeid(T));
|
|
}
|
|
|
|
template<typename T>
|
|
static type_id dynamic_type(const T& obj) {
|
|
return reinterpret_cast<type_id>(&typeid(obj));
|
|
}
|
|
|
|
template<class Stream>
|
|
static void type_name(type_id type, Stream& stream) {
|
|
stream << reinterpret_cast<const std::type_info*>(type)->name();
|
|
}
|
|
|
|
static std::type_index type_index(type_id type) {
|
|
return std::type_index(*reinterpret_cast<const std::type_info*>(type));
|
|
}
|
|
|
|
template<typename D, typename B>
|
|
static D dynamic_cast_ref(B&& obj) {
|
|
return dynamic_cast<D>(obj);
|
|
}
|
|
};
|
|
----
|
|
|
|
* `static_type` is used by class registration, by `virtual_ptr`{empty}'s "final"
|
|
constructs, and to format error and trace messages. `T` is not restricted to
|
|
the classes that appear as virtual parameters. This function is required.
|
|
|
|
* `dynamic_type` is used to locate the v-table for an object. This function is
|
|
usually required. If only the `virtual_ptr` "final" constructs are used, or
|
|
if `boost_openmethod_vptr` is provided for all the classes in the policy, it
|
|
can be omitted.
|
|
|
|
* `type_name` writes a representation of `type` to `stream`. It is used to format
|
|
error and trace messages. `Stream` is a lighweight version of `std::ostream`
|
|
with reduced functionality. It only supports insertion of `const char*`,
|
|
`std::string_view`, pointers and `std::size_t`. This function is optional;
|
|
if it is not provided, "type_id(_type_)" is used.
|
|
|
|
* `type_index` returns an object that _uniquely_ identifies a class. Some forms
|
|
of RTTI (most notably, C++'s `typeid` operator) do not guarantee that the
|
|
type information object for a class is unique within the same program. This
|
|
function is optional; if not provided, `type` is assumed to be unique, and
|
|
used as is.
|
|
|
|
* `dynamic_cast_ref` casts `obj` to class `D`. `B&&` is either a lvalue reference
|
|
(possibly cv-qualified) or a rvalue reference. `D` has the same reference
|
|
category (and cv-qualifier if applicable) as `B`. This function is required
|
|
only in presence of virtual inheritance.
|
|
|
|
Consider a custom RTTI implementation:
|
|
|
|
[source,c++]
|
|
----
|
|
struct Animal {
|
|
Animal(unsigned type) : type(type) {
|
|
}
|
|
|
|
virtual ~Animal() = default;
|
|
|
|
unsigned type;
|
|
static constexpr unsigned static_type = 1;
|
|
};
|
|
|
|
struct Cat : Animal {
|
|
Cat() : Animal(static_type) {
|
|
}
|
|
|
|
static constexpr unsigned static_type = 2;
|
|
};
|
|
|
|
// ditto for Dog
|
|
----
|
|
|
|
This scheme has an interesting property: its type ids are monotonically
|
|
allocated in a small, dense range. Thus, we don't need to hash them. We can use
|
|
them as indexes in the table of vptrs.
|
|
|
|
This time we are going to replace the default policy globally. First we need to
|
|
define the custom RTTI facet. We must _not_ include
|
|
`<boost/openmethod/core.hpp>` or any header that includes it yet.
|
|
|
|
Here is the facet implementation:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{examplesdir}/custom_rtti.cpp[tag=facet]
|
|
----
|
|
|
|
This facet is quite minimal. It does not support virtual inheritance. It would
|
|
not produce good error or trace messages, because types would be represented by
|
|
their integer ids.
|
|
|
|
This time we create a policy from scratch. For that we use the `basic_policy`
|
|
CRTP template:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{examplesdir}/custom_rtti.cpp[tag=policy]
|
|
----
|
|
|
|
Now we can include the "core" header and write the example:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{examplesdir}/custom_rtti.cpp[tag=example]
|
|
----
|
|
|
|
This programs compiles even if standard RTTI is disabled.
|
|
|
|
## Deferred RTTI
|
|
|
|
In the previous example, the RTTI system assigns types id statically. It is more
|
|
common to allocate them using a global counter, manipulated by static
|
|
constructors. This is a problem, because `static_type` is used by class
|
|
registration. It may read the custom type ids _before_ they are have been
|
|
initialized.
|
|
|
|
The solution is to add the `deferred_static_rtti` facet to the policy; it defers
|
|
reading the type information until `initialize` is called.
|
|
|
|
This time let's support virtual inheritance as well. First the domain classes:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{examplesdir}/deferred_custom_rtti.cpp[tag=classes]
|
|
// ditto for Dog
|
|
----
|
|
|
|
The rtti facet is the same, with one more function:
|
|
|
|
[source,c++]
|
|
----
|
|
struct custom_rtti : bom::policies::rtti {
|
|
// as before
|
|
|
|
include::{examplesdir}/deferred_custom_rtti.cpp[tag=dynamic_cast_ref]
|
|
};
|
|
----
|
|
|
|
Finally, the policy contains an additional facet - `deferred_static_rtti`:
|
|
|
|
[source,c++]
|
|
----
|
|
struct custom_policy
|
|
: bom::policies::basic_policy<
|
|
custom_policy, custom_rtti,
|
|
bom::policies::deferred_static_rtti, // <-- additional facet
|
|
bom::policies::vptr_vector<custom_policy>> {};
|
|
----
|
|
|
|
The example is the same as in the previous section.
|