2009-07-24 22 views
19

Có thư viện nào hỗ trợ việc triển khai thiết kế theo nguyên tắc hợp đồng trong ứng dụng C++ không?Thư viện để tạo thuận lợi cho việc sử dụng nguyên tắc "thiết kế theo hợp đồng"

Cụ thể, tôi đang tìm một thư viện có cơ sở sử dụng nguyên tắc, chẳng hạn như this.

+2

Bạn nên làm rõ điều gì khiến bạn không hài lòng về các cơ chế đơn giản với macro khẳng định (chữ thường). –

+1

Xem thêm http://stackoverflow.com/questions/179723/what-is-the-best-way-of-implementing-assertion-checking-in-c –

+3

Bạn vừa liên kết với thư viện thực hiện chính xác những gì bạn đang yêu cầu. Bạn mong đợi chúng ta nói gì? "Bạn có thể cung cấp cho http://www.codeproject.com/KB/cpp/DesignByContract.aspx một ảnh"? Nếu bạn muốn một cái gì đó nhiều hơn những gì mà thư viện cung cấp, sau đó không sử dụng nó như là một ví dụ về những gì bạn đang sau. Cho chúng tôi biết những gì bạn muốn mà nó không cung cấp. – jalf

Trả lời

8

Tôi đi theo những lời dạy của các bài viết sau:

  • An exception or a bug? (Miro Samek, C/C Người dùng ++ Journal, 2003)
  • Hỗ trợ đơn giản để thiết kế bởi hợp đồng trong C++ (Pedro Guerreiro , TOOLS, 2001)

Điều cuối cùng tôi áp dụng là cách tiếp cận của Samek khá nhiều. Chỉ cần tạo macro cho REQUIRE, ENSURE, CHECK và INVARIANT (dựa trên macro hiện tại assert) rất hữu ích. Tất nhiên nó không phải là tốt như hỗ trợ ngôn ngữ bản địa nhưng dù sao, nó cho phép bạn để có được hầu hết các giá trị thực tế từ kỹ thuật này.

Đối với thư viện, tôi không nghĩ rằng nó trả tiền để sử dụng một, bởi vì một giá trị quan trọng của cơ chế khẳng định là sự đơn giản của nó.

Để biết sự khác biệt giữa gỡ lỗi và mã sản xuất, hãy xem When should assertions stay in production code?.

6

Đơn giản nhất?

Tuyên bố khẳng định khi bắt đầu chức năng của bạn để kiểm tra các yêu cầu của bạn. Phát biểu xác nhận ở cuối chức năng của bạn để kiểm tra kết quả của bạn.

Có, nó thô, nó không phải là một hệ thống lớn, nhưng sự đơn giản của nó làm cho nó linh hoạt và di động.

+0

Một cái gì đó tốt hơn so với http://www.codeproject.com/KB/cpp/DesignByContract này.aspx – yesraaj

+6

Và tính năng chính của DbC - trước/sau điều kiện được kế thừa - sẽ không được mô phỏng bởi Assert. –

+4

Ngoài ra "ở cuối chức năng của bạn" trở nên rất phức tạp khi bạn sử dụng "trả lại" ở một vài nơi. Chưa kể ngoại lệ. –

6

Một số mẫu thiết kế, chẳng hạn như non-virtual interface làm cho nó tự nhiên để viết trước/sau điều kiện cho một phương pháp đưa ra:

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

thực sự, để phù hợp với DbC, điều kiện tiên quyết phải là chức năng ảo (các lớp khởi tạo có thể làm giảm các điều kiện tiên quyết bằng cách thêm mã bổ sung). Ví dụ: void stop() {stop_prec(); stop_impl(); assert (! engine_running())} và stop_prec() là một hàm ảo với việc thực hiện (hầu hết các điều kiện trước cứng nhắc). Nhưng đề xuất của bạn nghe hay đấy! –

1

Sử dụng tiêu chuẩn ASSERT/Q_ASSERT, nhưng hãy cẩn thận của khẳng định "không hợp lệ", đặc biệt là nếu bạn để lại chẩn đoán như vậy trong thử nghiệm bên ngoài (xây dựng mà không cần NDEBUG).

Câu chuyện nhỏ liên quan đến việc triển khai DBC (sử dụng xác nhận) trong dự án C++ và chính sách "gỡ lỗi luôn được bật".

Chúng tôi đã sử dụng các công cụ khá chuẩn (ASSERT()/Q_ASSERT()) để triển khai DBC cho đến khi chúng tôi đạt được tình huống sau trong thử nghiệm tích hợp: phiên bản mới nhất của chúng tôi không thành công ngay sau khi bắt đầu. Nó không phải là rất chuyên nghiệp để phát hành phiên bản như vậy (sau tuần nỗ lực QA nội bộ).

Vấn đề đã được giới thiệu như thế nào?

  • Một nhà phát triển trái khẳng định sai (biểu Logic không hợp lệ) trong mã nguồn
  • Tất cả chúng tôi trước khi phát hành bản xây dựng có assertions bật (để theo dõi lỗi trong các thử nghiệm tích hợp)
  • nội QA có các cài đặt môi trường khác nhau so với các thử nghiệm tích hợp, do đó, "lỗi xác nhận" không hiển thị

Kết quả là poo r nhà phát triển bị đổ lỗi cho lỗi này (rõ ràng là không có ASSERT này sẽ không có sự cố) và chúng tôi phải phát hành hotfix để cho phép thử nghiệm tích hợp được tiếp tục.

Trước hết: Tôi cần assertions bật trong các thử nghiệm tích hợp để theo dõi tình trạng thất bại (các khẳng định càng nhiều càng tốt), mặt khác tôi không muốn các nhà phát triển để sợ rằng một số "phụ" ASSERT sẽ sụp đổ toàn bộ phần mềm ngăn xếp.

Tôi tìm thấy, có thể là giải pháp dựa trên C++ thú vị cho vấn đề này: xác nhận yếu. Ý tưởng là để không dừng toàn bộ ứng dụng trên xác nhận không thành công, nhưng để ghi lại stacktrace để phân tích sau này và tiếp tục. Chúng tôi có thể kiểm tra nhiều kỳ vọng như chúng tôi muốn mà không sợ bị lỗi và chúng tôi nhận phản hồi (stacktraces) từ tích hợp. Quá trình chạy đơn có thể cung cấp nhiều trường hợp xác nhận không thành công để phân tích thay vì chỉ một (vì không có hủy bỏ() được gọi).

Thực hiện ý tưởng này (sử dụng một số phép thuật LD_PRELOAD) được một thời gian ngắn mô tả ở đây: http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/

2

Nếu bạn không nhớ sử dụng C++0x features, bạn có thể thực hiện điều kiện tiên quyết và hậu sử dụng lambdas và RAII.

Một ví dụ đơn giản của hậu:

struct __call_on_destructor { 
    std::tr1::function<void()> _function; 
    template<class Func> inline __call_on_destructor(Func func) { 
     _function = func; 
    } 
    inline ~__call_on_destructor() { 
     _function(); 
    } 
}; 

#define on_scope_exit(function) \ 
    __call_on_destructor PP_UNIQUE_LABEL(on_exit) (function) 

#define ensures(expression) \ 
    on_scope_exit([&]() { assert(expression); }) 

Tương tự, bạn có thể thực hiện điều kiện tiên quyết và bất biến. Mã được lấy từ C++0x Contracts library cực kỳ đơn giản.

+0

câu hỏi chính: làm thế nào là điều kiện tiên quyết/postconditions/bất biến kế thừa? –

2

Hãy thử cái này: Contract++. Nó đã được chấp nhận để Tăng (nhưng chưa được giao hàng).

2

Tôi có tiêu đề litle C++ với các yêu cầu, bảo hiểm và bất biến. Nó có ít hơn 400 loc và nên đáp ứng nhu cầu của bạn. Bạn có thể tìm thấy nó dưới dhc.hpp Nó báo cáo lỗi một cách hữu ích và có thể được biên dịch thông qua định nghĩa.

#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)); 
} 
Các vấn đề liên quan