2009-07-24 10 views
19

Esiste una libreria che aiuti a implementare il progetto in base al principio del contratto in un'applicazione C++?Libreria per facilitare l'utilizzo del principio "design per contratto"

In particolare, sto cercando una libreria che funzioni l'utilizzo del principio, qualcosa come this.

+2

È necessario chiarire cosa ti rende insoddisfatto dei meccanismi semplici con la macro assert (in minuscolo). –

+1

Vedi anche http://stackoverflow.com/questions/179723/questo-è-il-best-way-of-implementing-assertion-checking-in-c –

+3

Ti sei appena collegato a una libreria che fa esattamente quello che sei chiedere. Cosa ti aspetti che diciamo? "Potresti dare http://www.codeproject.com/KB/cpp/DesignByContract.aspx un colpo"? Se si desidera qualcosa di più di quello che offre la libreria, non utilizzarla come esempio di ciò che si sta cercando. Dicci cosa vuoi che non fornisca. – jalf

risposta

8

ho seguito gli insegnamenti dei seguenti articoli:

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

Ciò che alla fine ho applicato era praticamente l'approccio di Samek. La semplice creazione di macro per REQUIRE, ENSURE, CHECK e INVARIANT (basata sulla macro esistente assert) è stata molto utile. Ovviamente non è buono come il supporto per la lingua madre, ma in ogni caso, ti permette di ottenere gran parte del valore pratico dalla tecnica.

Per quanto riguarda le biblioteche, non penso che valga la pena utilizzarne una, perché un valore importante del meccanismo di asserzione è la sua semplicità.

Per la differenza tra il codice di debug e di produzione, vedere When should assertions stay in production code?.

6

Più semplice?

Dichiarare dichiarazioni all'inizio della funzione per testare le proprie esigenze. Dichiarazioni di asserzione alla fine della tua funzione per testare i risultati.

Sì, è grezzo, non è un grande sistema, ma la sua semplicità lo rende versatile e portatile.

+0

Qualcosa di meglio di questo http://www.codeproject.com/KB/cpp/DesignByContract.aspx – yesraaj

+6

E la caratteristica principale di DbC - che le pre/post-condizioni sono ereditate - non verrà emulata da Assert. –

+4

Anche "alla fine delle tue funzioni" diventa molto complesso quando usi "ritorno" in pochi punti. Per non parlare delle eccezioni. –

6

Alcuni modelli di progettazione, come il non-virtual interface rendono naturale per scrivere pre/post-condizioni per un dato metodo:

#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

in realtà, per essere in linea con DbC, la precondizione dovrebbe essere la funzione virtuale (le classi derivate possono ridurre le precondizioni aggiungendo codice aggiuntivo). Esempio: void stop() {stop_prec(); stop_impl(); assert (! engine_running())} e stop_prec() è una funzione virtuale con implementazione (pre-condizioni più rigide). Ma la tua proposta suona bene! –

1

Usa ASSERT Standard/Q_ASSERT, ma attenzione delle affermazioni "non valido", specialmente se si lascia tale diagnostica in test esterni (build senza NDEBUG).

Piccola storia riguardante l'implementazione DBC (utilizzando asserzioni) in un progetto C++ e una politica di "debugging sempre abilitato".

Stavamo usando strumenti piuttosto standard (ASSERT()/Q_ASSERT()) come implementazione DBC fino a quando abbiamo raggiunto la seguente situazione in fase di test di integrazione: la nostra ultima build era sempre fallendo subito dopo iniziare. Non è stato molto professionale rilasciare tale versione (dopo una settimana di sforzi interni di controllo qualità).

Come è stato introdotto il problema?

  • Uno sviluppatore sinistra affermazione sbagliata (espressione logica non valida) nel codice sorgente
  • Tutto il nostro pre-release build avevano affermazioni hanno permesso (per tenere traccia errori nei test di integrazione)
  • QA interno ha diverse impostazioni dell'ambiente rispetto ai test di integrazione, quindi "errore di asserzione" non era visibile

Come risultato cacca r lo sviluppatore è stato accusato di errore (ovviamente senza questo ASSERT non ci sarebbe stato alcun arresto anomalo) e abbiamo dovuto rilasciare l'hotfix per consentire il proseguimento dei test di integrazione.

Prima di tutto: ho bisogno affermazioni abilitati nel test di integrazione per monitorare le condizioni falliti (i più asserzioni e meglio), d'altra parte non voglio sviluppatori di abbiate paura che alcuni ASSERT "extra" arresteranno lo stack completo del software.

Ho trovato, probabilmente una soluzione interessante basata su C++ per questo problema: asserzioni deboli. L'idea è di non interrompere l'intera applicazione in caso di asserzione fallita, ma di registrare lo stacktrace per analisi successive e continuare. Possiamo verificare tutte le aspettative che vogliamo senza paura di crash e riceviamo feedback (stacktraces) dall'integrazione. L'esecuzione di un singolo processo può fornire molti casi di asserzione falliti per l'analisi invece di uno solo (perché non c'è abort() chiamato).

L'attuazione di questa idea (usando qualche magia LD_PRELOAD) viene brevemente descritto qui: http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/

2

Se non ti dispiace utilizzando C++0x features, si potrebbe implementare precondizioni e postcondizioni utilizzando lambdas e Raii.

Un semplice esempio di post-condizione:

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

Allo stesso modo, si potrebbe implementare presupposti e invarianti. Il codice è stato preso da un estremamente semplice C++0x Contracts library.

+0

domande principali: come sono ereditate le precondizioni/postcondizioni/invarianti? –

2

Prova questo: Contract++. È stato accettato per Boost (ma non è ancora stato spedito).

2

Ho una piccola intestazione C++ con requisiti, assicurazioni e invarianti. Ha meno di 400 loc e dovrebbe soddisfare le tue esigenze. Lo trovi sotto dhc.hpp Segnala errori in modo utile e può essere compilato tramite define.

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