// https://www.boost.org/doc/libs/1_92_0/libs/contract/doc/html/index.html

#include <boost/contract.hpp>

#include <cassert>
#include <iostream>
#include <string>

// Class invariants plus a deliberately violated precondition
class counter
{
  public:
    void invariant() const
    {
        BOOST_CONTRACT_ASSERT(value() >= 0);
    }

    counter() : value_(0)
    {
        boost::contract::check c = boost::contract::constructor(this)
            .postcondition([&] { BOOST_CONTRACT_ASSERT(value() == 0); });
    }

    void decrement()
    {
        boost::contract::check c = boost::contract::public_function(this)
            .precondition([&] { BOOST_CONTRACT_ASSERT(value() > 0); });

        --value_;
    }

    void increment()
    {
        boost::contract::check c = boost::contract::public_function(this);
        ++value_;
    }

    int value() const
    {
        return value_;
    }

  private:
    int value_;
};

int main()
{
    bool failed = false;
    boost::contract::set_precondition_failure(
        [&failed](boost::contract::from) { failed = true; throw std::string("precondition"); });

    counter c;
    c.increment();
    c.increment();
    c.decrement();

    std::cout << "value = " << c.value() << std::endl;
    assert(c.value() == 1);

    c.decrement();
    assert(c.value() == 0);

    // Decrementing below zero must trip the precondition handler
    try
    {
        c.decrement();
        assert(false && "precondition was not checked");
    }
    catch (const std::string &e)
    {
        std::cout << "caught expected failure: " << e << std::endl;
    }

    assert(failed);
    assert(c.value() == 0);

    return 0;
}
