====== validate/guarantee ======
Mit validate möchte ich gerne Garantien beschreiben, die der Aufruf einer Funktion oder Methode garantieren muss.
Das Ziel sollte sein, dass ein Objekt unabhängig von den Funktionsaufrufen einen validen Status besitzt.
Beispiel 1: Funktion
Eine Funktion wird mit einem Array gerufen. Die Funktion arbeitet, wenn das Array null ist oder drei Elemente besitzt.
func is code( array is double{} ptr )
{
validate !array || array.size() == 3;
body
{
validate a is array
{
double x = a[0];
double y = a[1];
double z = a[2];
return x+y+z <= 1;
}
else
return true;
}
}
Eine Funktion, die nun func aufruft, muss garantieren, dass der Übergabeparameter null ist oder ein Pointer auf ein Array mit genau 3 Elementen.
bla is code( array is double{} ptr )
{
validate !array || array.size() == 3;
body()
{
// array ist const, die Bedingung für den Parameter decken die
// verlangten Garantien für func ab.
return func( array )
}
main is code :=
{
a is double{ 1, 2, 3 };
func( a ); // lasst sich garantieren.
}
bzw.
toArray( l is double [] ) as double{} ptr : new double{}( l );
main is code :=
{
l is double[];
l.push( 1 );
l.push( 2 );
l.push( 3 );
a is double{} ptr( toArray( l ) ); // keine Garantien für a
validate( !a || a.size() == 3 )
{
func( a ); // func muss das nun nicht mehr selbst prüfen
// kann aber auch nicht aufgerufen werden, wenn es nicht
// geprüft wurde.
}
}
Beispiel 2:
Ein Objekt Polyline enthält ein Array mit Punkten. Es soll gelten, dass eine Polyline aus mindestens 2 Punkten ebsteht.
Polyline contains
{
points is Point{}
{
minsize is guarantee: points.size() > 1;
}
construct; // Fehler nicht kompilierbar, da dieser Konstruktor nicht garantiert, dass points.size > 1 ist
construct( start, target is Point ) // Korrektur
{
points.push( start );
points.push( target ); // kompiliert nicht: Garantiert nicht points.size() > 1;
}
construct( start, target is Point ) // Korrektur
{
points.push( start );
points.push( target );
state minsize; // Kompiler kann das zur Laufzeit prüfen (tests)
}
construct( list is Point[] ) // Korrektur
{
validate( list.size() > 2 ) // Dieser Konstruktor ist nur aufrufbar, wenn der Parameter diese Bedingung erfüllt
body
{
points.size = list.size;
list[] * points.push( value ); // Vergleiche Vektor * Skalar
state minsize; // Kompiler kann das zur Laufzeit prüfen (tests)
}
}
getFirst()
{
return points.front(); // Front verlangt, dass points mindestens 1 groß ist. Die Garantie kann points geben.
}
};
======= validate/state ======
Validate ist eine Laufzeitüberprüfung, state ist ein Statement, quasi ein assert bei dem der Compiler davon ausgeht, dass es korrekt ist, sofern er das Gegenteil nicht beweisen kann.
Eine State-Anweisung kann im Debuglauf geprüft werden und so Tests zum Scheitern bringen, wenn sich das Statement als Falsch erweist.
Garantien sind vergleichbar mit const. Wenn PolyLine garantiert, dass points aus mindestens zwei Punkten besteht, kann man Points als vom Typ "double{} PolyLine::minsize" verstehen, wobei PolyLine::minsize ein Attribut wie const darstellt.
Array::front verlangt, dass Array.size() > 0 ist. PolyLine::minsize Garantiert für points.size() > 1. Für alle Größen, die größer 1 sind, gilt, dass sie auch größer 0 ist. Polyline::points kann beim Aufruf von Array::front also nicht scheitern. Hier ist also kein validate oder eine sonstige Absicherung erforderlich: Es kann nicht scheitern.
====== Mit validate scopes einrichten ======
/******************************************************************************
Online C Compiler.
Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.
*******************************************************************************/
#include
int func() { return 1; }
int func2() { return 2; };
int main()
{
double dbl = 0.1;
(result, success, type) = func()
a = func@(); // funktion gibt Struktur mit Fehlercodes zurück { result, [resulttype, ]failcode, failvalue, failtype }
validate( a )
{
puts a;
}
else
puts a@.message;
if( !rc )
{
puts( "Jow" );
}
C++-Variante
if( auto foo = func(), bar=func2(); foo && bar && foo == bar )
{
printf( "Jow\n");
}
foo = func()
then print "jow";
else print "No jow";
i is int ptr;
validate( i )
{
// i ist in diesem Scope i ref.
}
validate( j from i )
{
// j ist int ref auf i#.
}
validate( foo=func(), bar=func2() )
{
// ruft foo::operator validate( foo ) auf, bei int wird also auf 0 geprüft.
foo == bar
then
{
puts "Jow";
}
}
else puts "Mecker";
validate( foo=func(), bar=func2(); foo == bar )
puts "Jow";
else puts "Mecker";
return 0;
}
======= operator validate ======
Operator Validate sollte für int immer true zurückgeben. für double wenn nicht INF oder NAN.
Ein Typ rc sollte true zurückgeben, wenn == 0, ein ptr wenn != 0.
Aufzurufen mit foo?
====== operator ?. ======
foo?.method() ruft method nur auf, wenn foo valide ist (z.B. ptr). Ist foo nicht valide, wird methode nicht aufgeufen, aber kein Fehler ausgelöst.