Typically, BOOST_OPENMETHOD is used in headers, while
BOOST_OPENMETHOD_CLASSES and BOOST_OPENMETHOD_OVERRIDE are used in
implementation files.
Let’s use a payroll application as an example. We have two roles: Employee and
Salesman, and a pay method that computes the monthly pay of an employee. We
want to override and call pay from multiple translation units, so we put it in
a header:
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
#endif // ROLES_HPP
BOOST_OPENMETHOD defines an inline function, so it can be called only once
in a translation unit. The include guards see to this.
Let’s write the override for "just" employees - they get a fixed salary.
BOOST_OPENMETHOD_OVERRIDE adds an overrider to the method. We don’t want to
add the same multiple times; that would create an ambiguity. Thus it should go
in an implementation file:
// employee.cpp
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
BOOST_OPENMETHOD_CLASSES(Employee)
Salesmen get a salary like employees, and on top get a commission on their
sales. The next function, available only in the body of an overrider, calls
the next most specialized overrider. It is similar to super in other
languages, except that it does not stand for an object, but for an overrider.
// salesman.cpp
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return next(emp) + emp->sales * 0.05; // base + commission
}
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
Paytime!
// main.cpp
#include "roles.hpp"
#include <iostream>
#include <boost/openmethod/initialize.hpp>
int main() {
boost::openmethod::initialize();
Employee bill;
Salesman bob; bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
In the previous example, we used next to call the super-overrider. We can also
call an overrider directly. To do this, we can declare the overrider in the
header, and define it in an implementation file:
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee>), double);
#endif // ROLES_HPP
Unlike function declarations, BOOST_OPENMETHOD_DECLARE_OVERRIDER cannot appear multiple times in a translation unit with the same arguments. Also, it requires the method itself to be defined prior using this macro.
Overriders are placed in overrider containers. An overrider container is a
class template named after the method, declared in the current namespace. It is
specialized for each overrider signature. Macro
BOOST_OPENMETHOD_OVERRIDER takes the same
arguments BOOST_OPENMETHOD_OVERRIDE, and expands to the corresponding
specialization of the overrider container. Containers have a static member
function fn that contains the body the overrider, provided by the user. We can
call the overrider for Employee like so:
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return BOOST_OPENMETHOD_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee> emp),
double)::fn(emp) +
emp->sales * 0.05; // base + commission
}
This is similar to a virtual function calling a base overrider. Virtual
functions don’t have the equivalent of Smalltalk’s or Python’s super, but
OpenMethod does, it’s next. It is almost always the right choice.
The exception is: when performance is critical, we may want to inline the call to the base overrider. BOOST_OPENMETHOD_INLINE_OVERRIDE defines the overrider as an inline function, and it can go in a header file:
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
BOOST_OPENMETHOD_INLINE_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
#endif // ROLES_HPP
With inlining the overrider for Salesman compiles to (clang-20, x64-linux):
movsd xmm0, qword ptr [rsi + 8]
mulsd xmm0, qword ptr [rip + .LCPI1_0]
addsd xmm0, qword ptr [rip + .LCPI1_1]
ret