mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 16:32:12 +00:00
167 lines
5.6 KiB
Plaintext
167 lines
5.6 KiB
Plaintext
|
|
:example: ../examples/custom_rtti
|
|
|
|
[#custom_rtti]
|
|
|
|
The original motivation for the policy mechanism is to make it possible to
|
|
interface OpenMethod with custom RTTI systems.
|
|
|
|
Stock registries use the `std_rtti` implementation of `rtti`.
|
|
Here is its full source:
|
|
|
|
[source,c++]
|
|
----
|
|
struct std_rtti : rtti {
|
|
template<class Registry>
|
|
struct fn {
|
|
template<class Class>
|
|
static constexpr bool is_polymorphic = std::is_polymorphic_v<Class>;
|
|
|
|
template<class Class>
|
|
static auto static_type() -> type_id {
|
|
return &typeid(Class);
|
|
}
|
|
|
|
template<class Class>
|
|
static auto dynamic_type(const Class& obj) -> type_id {
|
|
return &typeid(obj);
|
|
}
|
|
|
|
template<typename Stream>
|
|
static auto type_name(type_id type, Stream& stream) -> void {
|
|
stream << boost::core::demangle(
|
|
reinterpret_cast<const std::type_info*>(type)->name());
|
|
}
|
|
|
|
static auto type_index(type_id type) -> std::type_index {
|
|
return std::type_index(
|
|
*reinterpret_cast<const std::type_info*>(type));
|
|
}
|
|
|
|
template<typename D, typename B>
|
|
static auto dynamic_cast_ref(B&& obj) -> D {
|
|
return dynamic_cast<D>(obj);
|
|
}
|
|
};
|
|
};
|
|
----
|
|
|
|
* `is_polymorphic` is used to check if a class is polymorphic.
|
|
This template is required.
|
|
|
|
* `static_type` is called during class registration, by
|
|
`virtual_ptr`{empty}s "final" constructs.
|
|
It is also called to set `bad_call::method`.
|
|
This function is required.
|
|
|
|
* `dynamic_type` is used to read the dynamic type of a virtual argument.
|
|
If only the `virtual_ptr` "final" constructs are used, or if
|
|
`boost_openmethod_vptr` is provided for all the classes in the registry,
|
|
this function can be omitted.
|
|
|
|
* `type_name` writes a representation of a type to a
|
|
`LightweightOutputStream`.
|
|
It is used to format error and trace messages.
|
|
This function is optional; if it is not provided, _type_ is rendered as
|
|
"type_id(_type_)".
|
|
|
|
* `type_index` returns an object that _uniquely_ identifies a class.
|
|
Some forms of RTTI (most notably, C{plus}{plus}'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 an object to a class.
|
|
It takes a reference, and returns a reference of the same category (and
|
|
cv-qualifier if applicable) as `B`.
|
|
This function is required only in presence of virtual inheritance.
|
|
|
|
Let's rewrite the Node example using several variations of custom RTTIs.
|
|
|
|
In the first example, we use a "static" scheme, where the type ids are
|
|
compile-time constants:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/1/custom_rtti.cpp[tag=classes]
|
|
----
|
|
|
|
Let's define a `rtti` policy for this scheme. We are going to replace the
|
|
default registry globally, so we do _not_ include `<boost/openmethod/core.hpp>`
|
|
or `<boost/openmethod.hpp>` - only what we need to implement the policy:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/1/custom_rtti.cpp[tag=policy]
|
|
----
|
|
|
|
The policy's notion of "polymorphic" is different from C{plus}{plus}'s. Here, a
|
|
class is "polymorphic" if it derives from `Node`, meaning that `dynamic_type`
|
|
can extract the _runtime_ type of a virtual argument. In fact, this policy does
|
|
not require Node to be polymorphic in the C++ sense. If we remove the virtual
|
|
destructor and implement `value` as an open-method as well, the program will
|
|
still work.
|
|
|
|
The policy is quite minimal. It does not support virtual inheritance, because it
|
|
does not provide a `dynamic_cast_ref` function. It would not produce good error
|
|
or trace messages, because it does not provide a `type_name` function. Instead,
|
|
it relies on the `type_name` inherited from cpp:rtti::defaults[]. It
|
|
renders types as adorned integers, e.g. "type_id(2)". All non-"polymorphic"
|
|
types would be rendered the same way, as "type_id(0)".
|
|
|
|
cpp:rtti::defaults[] also provides a default implementation for `type_index`,
|
|
which simply returns its argument.
|
|
|
|
Now we need a policy to get a v-table pointer from an object. Our RTTI system
|
|
has an interesting property: its type ids are monotonically allocated in a
|
|
small, dense range. It means that we can use them as indexes in a vector.
|
|
`vptr_vector` is perfect for that. So here is our registry:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/1/custom_rtti.cpp[tag=registry]
|
|
----
|
|
|
|
Defining macro
|
|
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[BOOST_OPENMETHOD_DEFAULT_REGISTRY]
|
|
sets the default registry used by all library components that need one.
|
|
|
|
Next, we include the main header.
|
|
Because `BOOST_OPENMETHOD_DEFAULT_REGISTRY` is defined, its value is used
|
|
for the default registry.
|
|
|
|
The rest of the example is unchanged.
|
|
|
|
## Deferred RTTI
|
|
|
|
In the previous example, the RTTI system assigns type ids statically. Another
|
|
popular approach is to allocate type ids using a global counter, manipulated by
|
|
static constructors. Like so:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/2/custom_rtti.cpp[tag=classes]
|
|
----
|
|
|
|
The type ids are assigned in another translation unit:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/2/custom_rtti.cpp[tag=type_ids]
|
|
----
|
|
|
|
This is a problem, because `static_type` is called by
|
|
`BOOST_OPENMETHOD_CLASSES`, also during static construction. There no guarantee
|
|
regarding the order of execution of static constructors across several
|
|
translation units. `BOOST_OPENMETHOD_CLASSES` risks reading the type ids
|
|
_before_ they have been assigned.
|
|
|
|
The solution is to derive the rtti policy from `deferred_static_rtti`. Doing so
|
|
postpones reading the type ids until `initialize` is called:
|
|
|
|
[source,c++]
|
|
----
|
|
include::{example}/2/custom_rtti.cpp[tag=policy]
|
|
----
|