Error Handling

When an error is encountered, the program is terminated by a call to abort. If the policy contains an error_handler facet, it provides an error member function (or overloaded functions) to be called with an object identifying the error. The release and debug policies implement the error facet with default_error_handler, which wraps the error object in a variant, and calls a handler via a std::function. By default, it prints a description of the error to stderr in the debug policy, and does nothing in the release policy. The handler can be set with set_error_handler:

#include <iostream>
#include <variant>

#include <boost/openmethod.hpp>
#include <boost/openmethod/compiler.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::not_implemented_error>(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 facet 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::not_implemented_error& 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/compiler.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::not_implemented_error&) {
            std::cout << "not implemented\n";
        }
    }

    return 0;
}
spin
not implemented
spin

Stock facet throw_error_handler does this for all the exception types:

namespace boost::openmethod::policies {

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

} // namespace boost::openmethod::policies