图书馆以促进“合同设计”的使用 原则

发布于 2024-07-27 17:26:16 字数 1542 浏览 2 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

流殇 2024-08-03 17:26:16

我遵循以下文章的教导:

  • 异常还是错误?(Miro
    Samek,C/C++ 用户期刊,2003)
  • 对合同设计的简单支持
    在 C++ 中
    (Pedro Guerreiro,TOOLS,2001)

我最终应用的几乎是 Samek 的方法。 仅仅为 REQUIRE、ENSURE、CHECK 和 INVARIANT 创建宏(基于现有的 assert 宏)就非常有用。 当然,它不如母语支持那么好,但无论如何,它可以让您从该技术中获得大部分实用价值。

至于库,我认为使用它并不值得,因为断言机制的一个重要价值是它的简单性。

有关调试代码和生产代码之间的区别,请参阅 断言何时应保留在生产代码中?

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?.

¢好甜 2024-08-03 17:26:16

最简单?

在函数开始处断言语句来测试您的需求。
在函数末尾添加断言语句来测试结果。

是的,它很粗糙,不是一个大系统,但它的简单性使其具有多功能性和便携性。

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.

芯好空 2024-08-03 17:26:16

一些设计模式,例如非虚拟接口使其成为可能自然地为给定方法编写前置/后置条件:

#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() {
        /* ... */
    }
}

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() {
        /* ... */
    }
}
夜司空 2024-08-03 17:26:16

试试这个:Contract++。 它已被 Boost 接受(但尚未发货)。

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

写给空气的情书 2024-08-03 17:26:16

我有一个小的 c++ 标头,其中包含要求、保险和不变量。 它的位置不到 400 个,应该可以满足您的需求。 您可以在 dhc.hpp 下找到它。有用的方法,可以通过定义编译出来。

#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));
}

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));
}
寒江雪… 2024-08-03 17:26:16

使用标准 ASSERT/Q_ASSERT,但要注意“无效”断言,特别是如果您在外部测试中保留此类诊断(不使用 NDEBUG 进行构建)。

关于 C++ 项目中的 DBC 实现(使用断言)和“始终启用调试”策略的小故事。

我们使用非常标准的工具(ASSERT()/Q_ASSERT())作为 DBC 实现,直到我们在集成测试中遇到以下情况:我们最新的构建总是在启动后失败。 发布这样的版本并不是很专业(经过一周的内部质量检查工作)。

问题是如何引入的?

  • 开发人员在源代码中留下了错误的断言(无效的逻辑表达式)。
  • 所有的预发布版本都启用了断言(以跟踪集成测试中的错误)
  • 我们 与集成测试不同的环境设置,因此“断言错误”不可见。

结果,可怜的开发人员被归咎于这个错误(显然没有这个 ASSERT 就不会崩溃),我们必须发布修补程序以允许继续进行集成测试。

首先:我需要在集成测试中启用断言来跟踪失败的条件(断言越多越好),另一方面,我不希望开发人员害怕一些“额外”的 ASSERT 会使整个软件堆栈崩溃。

我发现,对于这个问题,基于 C++ 的解决方案可能很有趣:弱断言。 这个想法是在断言失败时不停止整个应用程序,而是记录堆栈跟踪以供以后分析并继续。 我们可以检查任意数量的期望,而不必担心崩溃,并且我们可以从集成中获得反馈(堆栈跟踪)。 单个进程运行可以提供许多失败的断言案例供分析,而不是只有一个(因为没有调用 abort())。

这里简要描述了这个想法的实现(使用一些 LD_PRELOAD 魔法): http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/

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/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文