¿Existen patrones establecidos para verificar las invariantes de clases en C++?comprobando invariantes en C++
Lo ideal es que las invariantes se verifiquen automáticamente al principio y al final de cada función de miembro público. Por lo que sé, C con clases proporcionó funciones de miembro especiales before
y after
, pero desafortunadamente, el diseño por contrato no era muy popular en ese momento y nadie, excepto Bjarne, usó esa característica, por lo que la eliminó.
Por supuesto, insertar manualmente las llamadas check_invariants()
al principio y al final de cada función de miembro público es tedioso y propenso a errores. Desde RAII es el arma de elección para tratar con excepciones, se me ocurrió con el siguiente esquema de definición de un verificador de invariancia como la primera variable local, y que corrector invariancia comprueba los invariantes, tanto en la construcción y destrucción de tiempo:
template <typename T>
class invariants_checker
{
const T* p;
public:
invariants_checker(const T* p) : p(p)
{
p->check_invariants();
}
~invariants_checker()
{
p->check_invariants();
}
};
void Foo::bar()
{
// class invariants checked by construction of _
invariants_checker<Foo> _(this);
// ... mutate the object
// class invariants checked by destruction of _
}
Pregunta # 0: ¿Supongo que no hay forma de declarar una variable local sin nombre? :)
Aún tendríamos que llamar manualmente al check_invariants()
al final del constructor Foo
y al comienzo del destructor Foo
. Sin embargo, muchos cuerpos de constructor y cuerpos de destructor están vacíos. En ese caso, ¿podríamos usar un invariants_checker
como último miembro?
#include <string>
#include <stdexcept>
class Foo
{
std::string str;
std::string::size_type cached_length;
invariants_checker<Foo> _;
public:
Foo(const std::string& str)
: str(str), cached_length(str.length()), _(this) {}
void check_invariants() const
{
if (str.length() != cached_length)
throw std::logic_error("wrong cached length");
}
// ...
};
Pregunta # 1: ¿Es válido para pasar al constructor this
invariants_checker
el que llama inmediatamente check_invariants
a través de ese puntero, aunque el objeto Foo
está todavía en construcción?
Pregunta # 2: ¿Ve algún otro problema con este enfoque? ¿Puedes mejorarlo?
Pregunta n. ° 3: ¿Es este enfoque nuevo o bien conocido? ¿Hay mejores soluciones disponibles?
No puede usar 'this' en la lista de inicializadores. Sin embargo, podrías usarlo en el cuerpo del constructor. – Benoit
@Benoit: ¿Qué quieres decir con * can not *? ¿Está estrictamente prohibido? ¿Invoca un comportamiento indefinido? – fredoverflow
Thorsten Ottesen (creo que fue) tenía una propuesta para Design By Contract. No llegó a despegar en la primera ronda, debido a la dificultad de decidir qué es interno y qué es una llamada externa (puede romper temporalmente la llamada interna invariante). Pero aún podría aparecer. No sé si se ha trabajado activamente. –