entre - ¿Qué son los contratos en C++ 17?
diferencias entre c++ y c++ 11 (3)
Estaba leyendo sobre los contratos en Pensamientos sobre C ++ 17 de B. Stroustrup y asistí a una pequeña presentación hablando sobre ellos, pero no estoy seguro de haberlos entendido realmente.
Así que tengo algunas interrogaciones y si es posible ilustrarlas con algunos ejemplos:
¿Los contratos son solo un mejor reemplazo del
assert()
clásicoassert()
y deberían usarse juntos? ¿Qué contratos se ponen realmente en términos simples para un desarrollador de software?¿Los contratos tendrían un impacto en cómo manejamos las excepciones? En caso afirmativo, ¿cómo deberíamos usar excepciones y contratos?
¿El uso de contratos implicaría una sobrecarga en el tiempo de ejecución? ¿Se nos permitirá desactivarlas en el código de lanzamiento?
Edición de la propuesta N4415 :
Un contrato de condición previa del operador de indexación de una clase Vector podría escribirse:
T& operator[](size_t i) [[expects: i < size()]];
De manera similar, un contrato posterior a la condición en un constructor de una clase ArrayView se podría expresar como:
ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];
Comencé con el enlace del documento original OP proporcionado. Hay algunas respuestas, supongo. Recomiendo encarecidamente comenzar con ese papel. Aquí está la versión de TL&DR:
Los contratos no son un mecanismo general de informe de errores, ni son sustitutos de los marcos de prueba. Más bien, ofrecen una medida de mitigación básica cuando un programa sale mal debido a la falta de coincidencia de expectativas entre las partes de un programa. Conceptualmente, los contratos se asemejan más a asertivos estructurados () integrados en el lenguaje, que se basan en las reglas de la semántica del lenguaje, por lo tanto, son la base para el análisis de programas y herramientas de principios.
Acerca de sus preguntas:
- Está estructurado assert (), por lo que sí, se puede decir que en algunos casos assert () debe ser reemplazado por un contrato.
- Déjame usar otra cita aquí:
... la expresión de un contrato debe ser lógicamente parte de la declaración de la operación.
y ejemplo:
T& operator[](size_t i) [[expects: i < size()]];
En mi opinión, esto es simplemente agradable y legible.
- En algunos casos, los contratos pueden reemplazar las excepciones:
Sin embargo, es un criterio de diseño crítico que los contratos sean utilizables en sistemas integrados u otros sistemas con recursos limitados que no puedan permitirse excepciones.
En los contratos de condición previa todavía se pueden utilizar excepciones, ya que no se garantiza un comportamiento posterior después de la falla del contrato de condición previa.
- La sobrecarga se puede reducir activando / desactivando la verificación de contratos en casos: usar todo, usar no, solo condición previa, solo condición posterior. Los contratos activados definitivamente agregarán algunos gastos generales, como cualquier tipo de cheques.
Algunos casos de uso (como puedo suponer, aunque ni siquiera estoy cerca de desarrollar un diseño de contratos)
- Contratos: en el caso de
assert()
habitual, ya que los contratos son más legibles y pueden optimizarse en el momento de la compilación. - Afirmaciones - en pruebas unitarias, marcos de prueba, etc.
- Excepciones: se pueden usar con contratos preacondicionados como se menciona en el artículo:
La condición previa de una operación se evalúa antes que cualquier otra declaración en el cuerpo de la función. Si el resultado es verdadero, entonces el control normal de la ejecución continúa hasta la primera declaración en el cuerpo de la función. De lo contrario, no se garantiza una ejecución adicional: el programa cancela, o lanza una excepción, o si se le permite continuar, el comportamiento no está definido.
También hay other sugerencias sobre la implementación de contratos, por lo que nuestra investigación es prematura.
Hasta donde he leído de este documento: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf
Los contratos hacen lo que assert
ha estado tratando de hacer de manera primitiva durante años. Ambos son documentación y una afirmación en tiempo de ejecución de cómo se debe esperar que la persona que llama llame a la función y en qué estado puede esperar la persona que llama el código después de que la función haya regresado. Esas son usualmente conocidas como pre-condiciones y post-condiciones, o invariantes.
Esto ayuda a limpiar el código en el lado de la implementación, ya que con los contratos podemos suponer que una vez que la ejecución haya entrado en su función, sus argumentos estarán en un estado válido (lo que espera que sean).
La parte posterior a las condiciones puede cambiar la forma en que maneja las excepciones, ya que con los contratos tendrá que asegurarse de que lanzar una excepción no rompa sus post-condiciones. Por lo general, esto significa que su código debe ser seguro a excepción, aunque si eso significa una garantía de excepción sólida o una garantía básica depende de sus condiciones.
Ejemplo:
class Data; class MyVector { public: void MyVector::push_back(Elem e) [[ensures: data != nullptr]] { if(size >= capacity) { Data* p = data; data = nullptr; // Just for the sake of the example... data = new Data[capacity*2]; // Might throw an exception // Copy p into data and delete p } // Add the element to the end } private: Data* data; // other data };
En este ejemplo aquí, si el constructor new
o de Data
lanza una excepción, se viola su condición posterior. ¡Esto significa que debe cambiar todos estos códigos para asegurarse de que su Contrato nunca se viole!
Por supuesto, al igual que assert
, los contratos pueden incluir una sobrecarga de tiempo de ejecución. Sin embargo, la diferencia es que, dado que los contratos se pueden incluir como parte de la declaración de la función, el compilador puede realizar mejores optimizaciones, como evaluar las condiciones en el sitio de la persona que llama o incluso evaluarlas en el momento de la compilación. La sección 1.5 del documento mencionado al principio de este post habla sobre las posibilidades de desactivar contratos dependiendo de la configuración de su compilación, al igual que las afirmaciones anteriores.
No es fácil responder a tus preguntas que con: Depende. Esto se debe a que aún no está claro qué contratos serán exactamente. Hay varias propuestas e ideas dando vueltas en este momento:
n. 4378 Lakos et al. Básicamente propone estandarizar un sofisticado conjunto de herramientas de afirmación. Los contratos se verifican dentro de una implementación de funciones, se proporcionan 3 niveles de aserciones diferentes para controlar la cantidad de verificaciones en tiempo de ejecución y se puede personalizar el manejo de las infracciones de aserciones.
n4415 dos Reis et al. y n4435 Brown son bastante similares y proponen una sintaxis basada en atributos para definir condiciones previas y posteriores en las interfaces de funciones. No entran en detalles sobre la cantidad de control que otorgan durante las verificaciones en tiempo de ejecución y el comportamiento de las violaciones.
También ha habido trabajos menos recientes sobre este tema. Hay muchos detalles que aún no están decididos, y esta característica toca muchas áreas diferentes (por ejemplo, módulos, optimización, construcción / enlace) sobre algunas de las cuales el estándar tiene poco control.
Su pregunta sobre las excepciones es particularmente difícil, ya que las interacciones entre el manejo de las infracciones contractuales y las excepciones no son claras (por ejemplo, ¿podría un manejador de infracciones contractuales (útil en marcos de prueba)? ¿Qué pasa si la función es noexcept(true)
?).