Error Handling

Errors can occur during initialize, or during method dispatch, if the method’s registry contains the runtime_checks policy. If the registry contains an error_handler policy, its error_handler::error member function is called with a variant containing an error object, before terminating the program with a call to abort. default_registry contains such a policy: default_error_handler. It wraps the error object in a variant, and calls a handler via a std::function. By default, it prints a short description of the error to stderr, but this can be changed, for example, to throw an exception:

#include <iostream>
#include <variant>

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

using boost::openmethod::virtual_ptr;

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

struct Cat : Animal {};
struct Dog : Animal {};

BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog);

BOOST_OPENMETHOD(trick, (std::ostream&, virtual_ptr<Animal>), void);

BOOST_OPENMETHOD_OVERRIDE(
    trick, (std::ostream & os, virtual_ptr<Dog> /*dog*/), void) {
    os << "spin\n";
}

auto main() -> int {
    namespace bom = boost::openmethod;
    bom::initialize();

    bom::default_registry::error_handler::set([](const auto& error) {
        if (std::holds_alternative<bom::no_overrider>(error)) {
            throw std::runtime_error("not implemented");
        }
    });

    Cat felix;
    Dog hector, snoopy;
    std::vector<Animal*> animals = {&hector, &felix, &snoopy};

    for (auto animal : animals) {
        try {
            trick(std::cout, *animal);
        } catch (std::runtime_error& error) {
            std::cerr << error.what() << "\n";
        }
    }

    return 0;
}

Output:

spin
not implemented
spin

We can also replace the error_handler policy with our own. For example:

#include <iostream>

#include <boost/openmethod/default_registry.hpp>

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

struct Cat : Animal {};
struct Dog : Animal {};

namespace bom = boost::openmethod;

struct throw_if_not_implemented : bom::policies::error_handler {
    template<class Registry>
    struct fn {
        static auto error(const bom::openmethod_error&) -> void {
        }

        static auto error(const bom::no_overrider& err) -> void {
            throw err;
        }
    };
};

struct custom_registry : bom::default_registry::with<throw_if_not_implemented> {
};

#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry

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

using boost::openmethod::virtual_ptr;

BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog);

BOOST_OPENMETHOD(trick, (std::ostream&, virtual_ptr<Animal>), void);

BOOST_OPENMETHOD_OVERRIDE(
    trick, (std::ostream & os, virtual_ptr<Dog> /*dog*/), void) {
    os << "spin\n";
}

auto main() -> int {
    bom::initialize();

    Cat felix;
    Dog hector, snoopy;
    std::vector<Animal*> animals = {&hector, &felix, &snoopy};

    for (auto animal : animals) {
        try {
            trick(std::cout, *animal);
        } catch (bom::no_overrider&) {
            std::cout << "not implemented\n";
        }
    }

    return 0;
}
spin
not implemented
spin

Stock policy throw_error_handler does this for all the error types:

struct throw_error_handler : error_handler { template<class Error>
    [[noreturn]] static auto error(const Error& error) -> void { throw
    error; }
};

} // namespace boost::openmethod::policies