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