Untitled :: Boost.OpenMethod

A method can have more than one virtual parameter. This is often called "multi-method" or "multiple dispatch". All the virtual parameters participate equally in overrider selection, following the same rules as those governing overload resolution - except that the selection happens at runtime, and takes into account the arguments dynamic types.

Multiple dispatch is occasionally useful. When it is needed, it can be difficult to implement correctly and efficiently by hand. For example, given the following classes:

    struct Role {
        virtual ~Role() = default;
    };

    struct Employee : Role {};
    struct Salesman : Employee {};
    struct Manager : Employee {};
    struct Founder : Role {};

    struct Expense {
        virtual ~Expense() {
        }
    };

    struct Public : Expense {};
    struct Bus : Public {};
    struct Metro : Public {};
    struct Taxi : Expense {};
    struct PrivateJet : Expense {};

We want to implement a function - `approve` - that determines who can make what kind of expenses. The rules are:

- By default, an expense is rejected.

- Employees can take any public transportation.

- Managers can also take a taxi, for a ride cost up to $100.

- Founders can take any transportation, including a private jet.

This is a case of multiple dispatch: the outcome depends on two parameters. It can be implemented as a method with two virtual arguments. The four rules can be expressed as four overriders:

    using boost::openmethod::virtual_ptr;

    BOOST_OPENMETHOD(
        approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
        bool);

    BOOST_OPENMETHOD_OVERRIDE(
        approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
        bool) {
        return false;
    }

    BOOST_OPENMETHOD_OVERRIDE(
        approve, (virtual_ptr<const Employee>, virtual_ptr<const Public>, double),
        bool) {
        return true;
    }

    BOOST_OPENMETHOD_OVERRIDE(
        approve,
        (virtual_ptr<const Manager>, virtual_ptr<const Taxi>, double amount),
        bool) {
        return amount < 100.0;
    }

    BOOST_OPENMETHOD_OVERRIDE(
        approve, (virtual_ptr<const Founder>, virtual_ptr<const Expense>, double),
        bool) {
        return true;
    }

    int main() {
        boost::openmethod::initialize();

        Founder bill;
        Employee bob;
        Manager alice;

        Bus bus;
        Taxi taxi;
        PrivateJet jet;

        std::cout << std::boolalpha;
        std::cout << approve(bill, bus, 4.0) << "\n";        // true
        std::cout << approve(bob, bus, 4.0) << "\n";         // true
        std::cout << approve(bob, taxi, 36.0) << "\n";       // false
        std::cout << approve(alice, taxi, 36.0) << "\n";     // true
        std::cout << approve(alice, taxi, 2000.0) << "\n";   // false
        std::cout << approve(bill, jet, 120'000.0) << "\n";  // true
        std::cout << approve(bob, jet, 120'000.0) << "\n";   // false
        std::cout << approve(alice, jet, 120'000.0) << "\n"; // false
    }

Because `approve` understands inheritance, we don’t have to specify an overrider for every combination (5 role classes x 6 expense classes = 30 combinations).

### Ambiguities

Just like with overload resolution, ambiguities can arise. Let’s look at another example - matrix addition:

    #include <boost/openmethod.hpp>
    #include <boost/openmethod/initialize.hpp>
    #include <iostream>

    struct Matrix { virtual ~Matrix() = default; };
    struct DenseMatrix : Matrix {};
    struct SparseMatrix : Matrix {};

    BOOST_OPENMETHOD_CLASSES(Matrix, DenseMatrix, SparseMatrix);

    using boost::openmethod::virtual_ptr;

    BOOST_OPENMETHOD(
        add, (virtual_ptr<const Matrix>, virtual_ptr<const Matrix>), void);

    BOOST_OPENMETHOD_OVERRIDE(
        add, (virtual_ptr<const Matrix>, virtual_ptr<const SparseMatrix>), void) {
    }

    BOOST_OPENMETHOD_OVERRIDE(
        add, (virtual_ptr<const SparseMatrix>, virtual_ptr<const Matrix>), void) {
    }

    int main() {
        boost::openmethod::initialize();

        SparseMatrix a, b;
        add(a, b);
    }

The program terminates with the following error message:

    ambiguous
    Aborted (core dumped)

This is because the call to `add(a, b)` is ambiguous: both overriders are equally good matches. The solution is to add an overrider for the case where both arguments are `SparseMatrix`:

    BOOST_OPENMETHOD_OVERRIDE(
        add, (virtual_ptr<const SparseMatrix>, virtual_ptr<const SparseMatrix>), void) {
    }

Edit this Page