Core API
OpenMethod provides a macro-free interface: the core API. This is useful in certain situations, for example when combining open-methods and templates.
Let’s rewrite the Animals example using the core API. An open-method is
implemented as an instance of the method
template. Its parameters are a
function signature and a return type:
#include <boost/openmethod/core.hpp>
using namespace boost::openmethod;
class poke_openmethod;
using poke = method<
poke_openmethod(std::ostream&, virtual_<Animal&>), void>;
The poke_openmethod
class acts as the method’s identifier: it separates it
from other methods with the same signature. The exact name does not really
matter, and the class needs not be defined, only declared. Inventing a class
name can get tedious, so OpenMethod provides a macro for that:
#include <boost/openmethod/macros.hpp>
class BOOST_OPENMETHOD_ID(poke);
using poke = method<
BOOST_OPENMETHOD_ID(poke),
auto(std::ostream&, virtual_ptr<Animal>)->void>;
BOOST_OPENMETHOD and associated macros use BOOST_OPENMETHOD_ID in
their implementation. This makes it possible to mix the "macro" and "core"
styles.
|
We call the method via the nested function object fn
:
poke::fn(std::cout, animal);
Overriders are ordinary functions, added to a method using the nested template
override
:
auto poke_cat(std::ostream& os, virtual_ptr<Cat> /*cat*/) {
os << "hiss";
}
static poke::override<poke_cat> override_poke_cat;
override can register multiple overriders.
|
In C++26, we will be able to use _
instead of inventing a one-time-use
identifier. In the meantime, OpenMethod provides a small convenience macro:
#include <boost/openmethod/macros.hpp>
auto poke_dog(std::ostream& os, virtual_ptr<Dog> /*dog*/) {
os << "bark";
}
BOOST_OPENMETHOD_REGISTER(poke::override<poke_dog>);
next
is available from the method’s nested next
template:
auto poke_bulldog(std::ostream& os, virtual_ptr<Bulldog> dog) -> void {
poke::next<poke_bulldog>(os, dog);
os << " and bite";
}
BOOST_OPENMETHOD_REGISTER(poke::override<poke_bulldog>);
Since the function uses itself as a template argument in its body, its return type cannot be deduced. It must be specified explicitly, either by using the old function declaration style or a trailing return type. |
Why not call poke_dog
directly? We could; however, keep in mind that, in a
real program, a translation unit is not necessarily aware of the overriders
added elsewhere - especially in presence of dynamic loading.
We register the classes with use_classes
:
BOOST_OPENMETHOD_REGISTER(use_classes<Animal, Cat, Dog, Bulldog>);
Finally, we call the method via the static member of the method class fn
:
auto main() -> int {
boost::openmethod::initialize();
std::unique_ptr<Animal> a(new Cat);
std::unique_ptr<Animal> b(new Dog);
std::unique_ptr<Animal> c(new Bulldog);
poke::fn(std::cout, *a); // prints "hiss"
std::cout << "\n";
poke::fn(std::cout, *b); // prints "bark"
std::cout << "\n";
poke::fn(std::cout, *c); // prints "bark and bite"
std::cout << "\n";
return 0;