2009-07-24 12 views
19

¿Existe alguna biblioteca que ayude a implementar el principio de diseño por contrato en una aplicación C++?Biblioteca para facilitar el uso del principio "diseño por contrato"

En particular, estoy buscando una biblioteca que facilite el uso del principio, algo así como this.

+2

Debe aclarar qué lo deja insatisfecho con los mecanismos simples con la macro assert (minúsculas). –

+1

Ver también http://stackoverflow.com/questions/179723/what-is-the-best-way-of-implementing-assertion-checking-in-c –

+3

Acabas de vincular a una biblioteca que hace exactamente lo que eres preguntando por. ¿Qué esperas que digamos? "¿Podría darle una oportunidad a http://www.codeproject.com/KB/cpp/DesignByContract.aspx"? Si quieres algo más de lo que ofrece esa biblioteca, entonces no lo uses como un ejemplo de lo que estás buscando. Cuéntanos lo que quieres que no brinde. – jalf

Respuesta

8

que siguieron las enseñanzas de los siguientes artículos:

  • An exception or a bug? (Miro Samek, C/C++ Diario de Usuarios, 2003)
  • Soporte simple para Diseño por contrato en C++ (Pedro Guerreiro , TOOLS, 2001)

Lo que finalmente apliqué fue prácticamente el enfoque de Samek. Solo la creación de macros para REQUIRE, ASSURE, CHECK y INVARIANT (basado en la macro existente assert) fue muy útil. Por supuesto, no es tan bueno como el soporte del idioma nativo, pero de todos modos, le permite obtener la mayor parte del valor práctico de la técnica.

En cuanto a las bibliotecas, no creo que valga la pena usar una, porque un valor importante del mecanismo de afirmación es su simplicidad.

Para la diferencia entre el código de depuración y de producción, vea When should assertions stay in production code?.

6

¿Más simple?

Realice las afirmaciones al comienzo de su función para comprobar sus requisitos. Afirma las afirmaciones al final de tu función para probar tus resultados.

Sí, es crudo, no es un gran sistema, pero su simplicidad lo hace versátil y portátil.

+0

Algo mejor que esto http://www.codeproject.com/KB/cpp/DesignByContract.aspx – yesraaj

+6

Y Assert no emulará la característica principal de DbC, que las condiciones previas/posteriores se heredan. –

+4

También "al final de sus funciones" se vuelve muy complejo cuando utiliza "devolver" en pocos lugares. Sin mencionar excepciones. –

6

Algunos patrones de diseño, como el non-virtual interface hacen que sea natural para escribir pre/post-condiciones para un determinado método:

#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

en realidad, para estar en línea con DbC, la condición previa debe ser la función virtual (las clases derivadas pueden disminuir las condiciones previas agregando código adicional). Ejemplo: void stop() {stop_prec(); stop_impl(); assert (! engine_running())} y stop_prec() es una función virtual con implementación (la mayoría de condiciones rígidas). ¡Pero tu propuesta suena bien! –

1

Uso ASSERT estándar/Q_ASSERT, pero cuidado con las afirmaciones "no válido", especialmente si deja tales diagnósticos en pruebas externas (compilación sin NDEBUG).

Pequeña historia sobre la implementación de DBC (utilizando aserciones) en un proyecto de C++ y la política de "depuración siempre habilitada".

Estábamos usando herramientas bastante estándar (ASSERT()/Q_ASSERT()) como implementación DBC hasta que llegamos a la siguiente situación en las pruebas de integración: nuestra última compilación siempre falla después de iniciar. No fue muy profesional lanzar dicha versión (después de la semana de esfuerzos internos de control de calidad).

¿Cómo se presentó el problema?

  • Un desarrollador izquierda afirmación equivocada (expresión lógica no válida) en el código fuente
  • Toda nuestra compilación anterior tenían afirmaciones habilitadas (para rastrear errores en las pruebas de integración)
  • de control de calidad interno tiene diferentes configuraciones de entorno que las pruebas de integración, por lo que el "error de aserción" no era visible

Como resultado poo r se culpó al desarrollador de por este error (obviamente sin este ASSERT no habría bloqueo) y tuvimos que lanzar el hotfix para permitir que continuaran las pruebas de integración.

En primer lugar: Necesito afirmaciones habilitadas en las pruebas de integración para rastrear las condiciones fallidos (las afirmaciones más mejor), por otra parte, que no quiero a los desarrolladores tengan miedo de que algunos afirman "extra" bloqueará la pila completa de software.

Encontré, probablemente interesante resolución basada en C++ para este problema: aserciones débiles. La idea es para no detener la aplicación completa en la aserción fallida, pero para registrar stacktrace para un análisis posterior y continuar. Podemos verificar todas las expectativas que queramos sin temor a bloqueos y obtenemos retroalimentación (stacktraces) de la integración. La ejecución de un solo proceso puede proporcionar muchos casos de aserción fallida para análisis en lugar de solo uno (porque no se llama a abort()).

La aplicación de esta idea (con un poco de magia LD_PRELOAD) se describe brevemente aquí: http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/

2

Si no les importa usar C++0x features, se podría aplicar condiciones previas y condiciones posteriores utilizando lambdas y RAII.

Un ejemplo sencillo de postcondition:

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

Del mismo modo, se podría aplicar precondiciones e invariantes. El código fue tomado de un extremadamente simple C++0x Contracts library.

+0

preguntas principales: ¿cómo se heredan las precondiciones/postcondiciones/invariantes? –

2

Pruebe este: Contract++. Ha sido aceptado en Boost (pero aún no se envía).

2

Tengo un encabezado de C++ con requisitos, seguros e invariantes. Tiene menos de 400 loc y debe satisfacer sus necesidades. Puede encontrarlo en dhc.hpp Informa errores de una manera útil y se puede compilar a través de 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)); 
} 
Cuestiones relacionadas