yorel::yomm2::method
defined in <yorel/yomm2/core.hpp>, also provided by <yorel/yomm2/keywords.hpp>
template<
typename Key, typename ReturnType, typename... Args,
class Policy = default_policy
>
struct method; // not defined
template<typename Key, typename ReturnType, typename... Args, class Policy>
struct method<Key, ReturnType(Args...), Policy>;
method
provides a static function object, fn
, that takes a list of arguments
of type Args
, minus the virtual_
decorator, and returns ReturnType
.
Method definitions can be added with the method::add_function
and method::add_definition
class templates.
Key: a type that differentiates methods with the same signature. It is recommended to declare a class (there is no need to define it) for each method name in the same namespace. YOMM2_SYMBOL can be used for that effect.
ReturnType: the type of the value returned by the function, or void. May
not be auto
.
Args: the types of the method’s parameters. At least one parameter must be decorated with virtual_.
Policy: the policy of the method. The method is added to the policy’s
method list; its dispatch characteristics are determined by the policy. If not
specified, default_policy
is used.
Name | Description |
---|---|
constructor | construct and register the method |
destructor | destruct and unregister the method |
operator() | call the method |
method<Key, R(Args...)>::method();
Add the method to the policy’s method list.
method<Key, R(Args...)>::~method();
Remove the method from the policy’s method list.
method<Key, R(Args...)>::operator()(args...);
Call the method. The dynamic types of the arguments corresponding to a virtual_ parameter determine which method definition to call.
Name | Description |
---|---|
fn | function object to call the method |
method<Key, R(Args...)>::fn;
The single instance of method<Key, R(Args...)>
. Used to call the method.
Name | Description |
---|---|
add_function | add a definition to the method |
add_definition | add a definition container to the method |
next_type | type of a pointer to the next most specialised definition |
use_next | CRTP base for definitions that use next |
template<auto Function>
struct add_function {
explicit add_function(next_type* next = nullptr);
};
Register Function
as a definition of the method
. If specified, next
is
set to a pointer to the next most specialised definition, or to an error
handler if the next definition does not excist, or is ambiguous.
The parameters of Function
must be compatible with the corresponding
parameters in the method when virtual, and invariant otherwise. The return
type of Function
must be compatible with the return type of the method.
template<typename Container>
struct add_definition {
add_definition();
};
Register static member function Container::fn
as a definition of the
method
. If static member variable Container::next
exists, it is set to a
pointer to the next most specialised definition, or to an error handler if
the next definition does not exist, or is ambiguous.
The parameters of Container::fn
must be compatible with the corresponding
parameters in the method when virtual, and invariant otherwise. The return
type of Function
must be compatible with the return type of the method.
template<typename Key, typename R, typename... Args>
struct method<Key, R(Args...)> {
using next_type = unspecified;
};
Register Function
as a definition of the method
. If specified, next
is
set to a pointer to the next most specialised definition, or to an error
handler if the next definition does not excist, or is ambiguous.
The parameters of Function
must be compatible with the corresponding
parameters in the method when virtual, and invariant otherwise. The return
type of Function
must be compatible with the return type of the method.
template<typename Container>
struct use_next {
static next_type next;
};
template<typename Key, typename R, typename... A, typename... Unspecified>
template<typename Container>
typename method<Key, R(A...), Unspecified...>::next_type
method<Key, R(A...), Unspecified...>::use_next<Container>::next;
CRTP base class for definition containers that need to call the next most specialised method.
Static member variables must be declared inside the class definition, and
defined outside of it. This is cumbersome. use_next
declares, and defines,
a static next
function pointer, which can be injected in the container’s
scope via inheritance, to be picked up by add_container
. If this doesn’t
make sense, see the example below.
#include <yorel/yomm2/core.hpp>
#include <yorel/yomm2/symbols.hpp> // for YOMM2_GENSYM
#include <memory>
#include <string>
struct Animal { virtual ~Animal() {} };
struct Cat : Animal {};
struct Dog : Animal {};
struct Bulldog : Dog {};
namespace yomm2 = yorel::yomm2; // for brevity
using yomm2::virtual_;
YOMM2_STATIC(yomm2::use_classes<Animal, Cat, Dog, Bulldog>);
struct kick_methods;
using kick = yomm2::method<kick_methods, std::string(virtual_<Animal&>)>;
std::string kick_cat(Cat& dog) { return "hiss"; }
YOMM2_STATIC(kick::add_function<kick_cat>);
std::string kick_dog(Dog& dog) { return "bark"; }
YOMM2_STATIC(kick::add_function<kick_dog>);
struct kick_bulldog : kick::use_next<kick_bulldog> {
static std::string fn(Bulldog& dog) { return next(dog) + " and bite"; }
};
YOMM2_STATIC(kick::add_definition<kick_bulldog>);
struct YOMM2_SYMBOL(pet); // use obfuscated name
using pet = yomm2::method<YOMM2_SYMBOL(pet), std::string(virtual_<Animal&>)>;
std::string pet_cat(Cat& dog) { return "purr"; }
YOMM2_STATIC(pet::add_function<pet_cat>);
std::string pet_dog(Dog& dog) { return "wag tail"; }
YOMM2_STATIC(pet::add_function<pet_dog>);
BOOST_AUTO_TEST_CASE(ref_method_example) {
yomm2::update();
std::unique_ptr<Animal>
felix = std::make_unique<Cat>(),
snoopy = std::make_unique<Dog>(),
hector = std::make_unique<Bulldog>();
BOOST_TEST(kick::fn(*felix) == "hiss");
BOOST_TEST(kick::fn(*snoopy) == "bark");
BOOST_TEST(kick::fn(*hector) == "bark and bite");
BOOST_TEST(pet::fn(*felix) == "purr");
BOOST_TEST(pet::fn(*snoopy) == "wag tail");
BOOST_TEST(pet::fn(*hector) == "wag tail");
}