Methods are scoped in a registry. A method can only reference classes in the same registry. If a class is used as a virtual parameter in methods using different registries, it must be registered with each of them.

Class templates use_classes, method, virtual_ptr, and macros BOOST_OPENMETHOD and BOOST_OPENMETHOD_CLASSES, take an additional argument, a registry class, which defaults to default_registry. The default registry can be overridden by defining the macroprocessor symbol BOOST_OPENMETHOD_DEFAULT_REGISTRY before including <boost/openmethod/core.hpp>. The value of the symbol is used as a default template parameter for use_classes, method, virtual_ptr, and others. Once the core header has been included, changing BOOST_OPENMETHOD_DEFAULT_REGISTRY has no effect.

A registry has a collection of policies. Each policy belongs to a policy category. A registry may contain at most one policy of each category. Policies control how type information is obtained, how vptrs are acquired, how errors are handled and reported, etc. While the behavior of initialize can be customized via options, policies are primarily involved in method dispatch.

Policies are placed in the boost::openmethod::policies namespace.

default_registry contains the following policies:

policy category policy role

rtti

std_rtti

provides type information for classes and objects

type_hash

fast_perfect_hash

hashes type id to an index in a vector

vptr

vptr_vector

stores vptrs in an indexed collection

error_handler

default_error_handler

calls an overridable handler function

output

stderr_output

prints diagnostics to stderr

if BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS is defined, default_registry also contains the runtime_checks policy. This enables extra validations during method dispatch, which can detect missing class registrations that could not be caught by initialize.

The library provides another predefined registry: indirect_registry. It is useful when shared libraries are dynamically loaded at runtime, and add methods and overriders across program and shared library boundaries. See the section about shared libraries.

Registries can be created from scratch, using the registry template. Here is the definition of default_registry, copied from <boost/openmethod/registry.hpp>:

struct default_registry
    : registry<
          policies::std_rtti,
          policies::fast_perfect_hash, policies::vptr_vector,
          policies::default_error_handler,
          policies::stderr_output
#ifdef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
          ,
          policies::runtime_checks
#endif
          > {
};

When defining a new registry, it is recommended to define a new class, derived from registry<…​>, rather than via a typedef, which would create excessively long symbol names and make debugging harder.

That class is a convenience, not the registry’s identity. Everything a registry owns - the class and method lists, the dispatch tables, the state of every stateful policy - is keyed on the registry<…​> specialization the class derives from, which is what its registry_type member aliases. Two classes built from the same policies, in the same order, are therefore the same registry, and share everything:

struct animals : registry<policies::std_rtti, policies::vptr_map<>> {};
struct vehicles : registry<policies::std_rtti, policies::vptr_map<>> {};

// the policy lists are identical, so this is one registry, not two
static_assert(std::is_same_v<animals::registry_type, vehicles::registry_type>);

This is worth watching for when the purpose of a second registry is to isolate a set of methods from another, since such a registry would naturally be given the same policies as the first. Registering a class or a method in either would then register it in both. To keep them apart, give each one a policy of its own:

// a policy in a category of its own, carrying nothing but a number
struct marker_category {
    using category = marker_category;
};

template<int N>
struct marker final : marker_category {
    template<class Registry>
    struct fn {};
};

struct animals : default_registry::with<marker<1>> {};
struct vehicles : default_registry::with<marker<2>> {};

static_assert(!std::is_same_v<animals::registry_type, vehicles::registry_type>);

The order of the policies matters. When initialize runs, it calls each policy’s initialize in the order the policies appear in the registry, from left to right; finalize calls each policy’s finalize in the reverse order. A policy that depends on another policy having been initialized must therefore be listed after its dependency. In particular, vptr_vector reads the state of the type_hash policy, so the type_hash policy must come before vptr_vector — as in default_registry above, where fast_perfect_hash precedes vptr_vector. This particular requirement is enforced with a static_assert.

A registry can also be created by copying an existing registry’s policies, using the with and without nested templates. For example, indirect_registry is a tweak of default_registry:

struct indirect_registry : default_registry::with<policies::indirect_vptr> {};

Policies are implemented as unary Boost.MP11 quoted metafunctions. A policy is an ordinary class that contains a nested class template fn, which is instantiated by the registry, passing itself as the single template argument. The reason for this mechanism is to allow policies to have static data members, which each registry must have its own set of. vptr_vector, for example, stores v-table pointers in a std::vector. If it is used in two different registries, it needs to use two different vectors, one for each registry.