Library to facilitate the use of the "design by contract" principle [closed]
Asked Answered
T

6

20

Is there any library that aids in implementing the design by contract principle in a C++ application?

In particular, I'm looking for a library that facilities the usage of the principle, something like this.

Tart answered 24/7, 2009 at 6:58 Comment(3)
You should clarify what leaves you unsatisfied about simple mechanisms with the assert (lower case) macro.Carob
See also #180223Carob
You just linked to a library that does exactly what you're asking for. What do you expect us to say? "You could give codeproject.com/KB/cpp/DesignByContract.aspx a shot"? If you want something more than what that library offers, then don't use it as an example of what you're after. Tell us what you want that it doesn't provide.Rosiarosicrucian
C
10

I followed the teachings of the following articles:

  • An exception or a bug? (Miro Samek, C/C++ Users Journal, 2003)
  • Simple Support for Design by Contract in C++ (Pedro Guerreiro, TOOLS, 2001)

What I ultimately applied was pretty much Samek's approach. Just creating macros for REQUIRE, ENSURE, CHECK and INVARIANT (based on the existing assert macro) was very useful. Of course it's not as good as native language support but anyway, it allows you to get most of the practical value from the technique.

As for libraries, I don't think that it pays to use one, because one important value of the assertion mechanism is its simplicity.

For the difference between debug and production code, see When should assertions stay in production code?.

Carob answered 24/7, 2009 at 7:18 Comment(0)
F
6

Simplest?

Assert statements at the start of your function to test your requirements. Assert statements at the end of your function to test your results.

Yes, it's crude, its not a big system, but its simplicity makes it versatile and portable.

Filmy answered 24/7, 2009 at 7:2 Comment(4)
Something better than this codeproject.com/KB/cpp/DesignByContract.aspxTart
And the major feature of DbC - that pre/postconditions are inherited - will not be emulated by Assert.Leontina
Also "at the end of your functions" becomes very complex when you use "return" in few places. Not to mention exceptions.Perspicuous
True, you should at least use asserts at the beginning and each time you get data from elsewher (non-local) like when you get a pointer, you have to check that it's valid, when you get some values you have to check that they match your assumptions etc. The beginning asserts should purpose is to check the context of the function call, the other asserts check the assumptions about the extern data and the local operations.Shoddy
K
6

Some design patterns, such as the non-virtual interface make it natural to write pre/post-conditions for a given method:

#include <cassert>

class Car {
    virtual bool engine_running_impl() = 0;
    virtual void stop_impl() = 0;
    virtual void start_impl() = 0;

    public:
    bool engine_running() {
        return engine_running_impl();
    }

    void stop() {
        assert(engine_running());
        stop_impl();
        assert(! engine_running());
    }

    void start()
    {
        assert(! engine_running());
        start_impl();
        assert(engine_running());
    }
}


class CarImpl : public Car {
    bool engine_running_impl() {
        /* ... */
    }

    void stop_impl() {
        /* ... */
    }

    void start_impl() {
        /* ... */
    }
}
Kyd answered 24/7, 2009 at 20:32 Comment(1)
actually, to be in line with DbC, the precondition should be virtual function (deriving classes can lessen the preconditions by adding extra code). Example: void stop() { stop_prec(); stop_impl(); assert(!engine_running()) } and stop_prec() is a virtual function with implementation (most rigid pre-conditions). But your proposal sounds good!Leontina
H
2

Try this one: Contract++. It has been accepted to Boost (but is not shipping yet).

Hockett answered 11/10, 2013 at 13:25 Comment(0)
S
2

I have a litle c++ header with requirements, insurances and invariants. It has less than 400 loc and should fulfill your needs. You can find it under dhc.hpp It reports errors in a useful way and can be compiled out via defines.

#include <dbc.hpp>

class InvarTest {
public:
        int a = 0;
        int b = 9;

        INVARIANT_BEGIN
                Inv(RN(0,a,32));
                Inv(RN(0,b,10));
        INVARIANT_END

        inline void changeMethod() {
                Invariant(); // this runs the invariant block at the beginning and end of the method
                a = 33;         
        }
};

int testFunc(int a, double d, int* ip) {
        // RN = a in range 0 to 10, NaN = not a number, NN = not null
        Rqr(RN(0,a,10), NaN(d), RN(0.0,d,1.0), NN(ip));

        // Enr return the passed value
        return Esr(RN(0.0,a+d,20.3));
}

void testFunc2(std::vector<int>& a, std::shared_ptr<int> sp) {
        Rqr( SB(a,0), TE(a.size() % 12 == 0), NN(sp));
}
Spinks answered 14/1, 2014 at 11:4 Comment(0)
W
1

Use standard ASSERT/Q_ASSERT, but beware of "invalid" assertions, especially if you leave such diagnostics in external testing (build without NDEBUG).

Small story regarding DBC implementation (using assertions) in a C++ project and "debugging always enabled" policy.

We were using pretty standard tools (ASSERT()/Q_ASSERT()) as DBC implementation until we hit the following situation in integration testing: our latest build was always failing just after start. It was not very professional to release such version (after week of internal QA efforts).

How the problem was introduced?

  • A developer left wrong assertion (invalid logic expression) in source code
  • All our pre-release builds had assertions enabled (to track errors in integration tests)
  • Internal QA has different environment settings than integration tests, so the "assertion error" was not visible

As a result poor developer was blamed for this error (obviously without this ASSERT there would be no crash) and we had to release hotfix to allow integration tests to be continued.

First of all: I need assertions enabled in integration tests to track failed conditions (the more assertions the better), on the other hand I don't want developers to be afraid that some "extra" ASSERT will crash full software stack.

I found, probably interesting C++-based resolution for this problem: weak asserions. The idea is to not stop whole application on failed assertion, but to record stacktrace for later analysis and continue. We can check as many expectations as we like without fear of crash and we get feedback (stacktraces) from integration. Single process run can provide many failed assertion cases for analysis instead of only one (because there's no abort() called).

Implementation of this idea (using some LD_PRELOAD magic) is shortly described here: http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/

Woodpecker answered 23/9, 2012 at 20:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.