Boost logo

Boost :

Subject: [boost] [contract] Contracts for constexpr functions
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2016-08-13 21:56:00


Hello all,

A couple of the proposals I read on adding Contract Programming to the
C++ standard raise the question if contracts should be supported for
constexpr functions or not. I tough about this question a bit... but I
am still not sure of the answer. I wanted to discuss this topic with
this mailing list, especially with respect to what Boost.Contract can
currently do.

My general impression is that preconditions, postconditions, and class
invariants should useful for constexpr functions but noting that:

* Probably old values will not be useful in constexpr functions
because constexpr prevents its functions from having side-effects (so
it seems there will no variable visible outside of the function that
the function body can change, and those variables are usually the
candidates for making old copies).
* Subcontracting will never apply to constexpr functions because they
cannot be virtual.

For example:

    struct conststr { // No virtual function so no subcontracting.
        [[invariant: empty() == (size() == 0)]] // Useful class invariants.
        [[invariant: data() != nullptr]]

        template<unsigned N>
        constexpr conststr(char const (&s)[N])
            [[requires: s != nullptr]] // Useful preconditions.
           : data_(s), size_(N - 1)
        {}

        constexpr char back() const
            [[ensures(result): result == data()[size() - 1]]] //
Useful postconditions.
            // Old values never useful because constexpr has no side-effects?
        {
            return data_[size_ - 1];
        }

        constexpr unsigned size() const { return size_; }
        constexpr bool empty() const { return size_ == 0; }
        constexpr char const* data() const { return data_; }

    private:
        char const* data_;
        unsigned size_;
    };

That said, if I understand it correctly, the following are not allowed
from within constexpr functions:

1. (Literal) type local variables with non-trivial constexpr
destructors (see N3597 and subsequent proposals).
2. Try-catch statements (see N3597 and subsequent proposals).
3. Lambda functions (see N4487).

Reading a few proposals, it seems that supporting the above from
constexpr functions would be technically possible, but difficult to
implement (see N3597 and its subsequent proposals, N4487, etc.). I'm
not really familiar with these constexpr restrictions and
plans/discussions to relax them in future revisions of the standard...

Boost.Contract implementation uses all the above 1-2-3 respectively to:

a. Check class invariants and postconditions at function exit (using RAII).
b. Catch assertion failures as well as any other exception that might
occur while evaluating an asserted conditions and then call the
appropriate contract failure handler.
c. Conveniently program functors that check pre- and postconditions
within the contracted function.

Therefore Boost.Contract implementation does not currently support
contracts for constexpr functions.

--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk