FEB
7
2004

Argument Constraints in the Function Declaration

The most annoying thing when writing public APIs is that it's a lot of redundant typing. A good API implementation should check all arguments and return a useful error message if an argument is invalid. Good API documentation should document which arguments values are valid and which return values are possible. Both tasks are work-intensive and annoying for the developer, which is probably one of the reasons why there so many bad APIs.
Here is a Java snippet with Sun-style argument handling:

/**
 * Sets the discount as percentage.
 * @param percentage the discount as percentage
 * @throws IllegalArgumentException if percentage < 0 or percentage > 100
 */
void setDiscountPercentage(float percentage) {
	if ((percentage < 0.0) || (percentage > 100.0))
		throw new InvalidArgumentException("percentage must be >=0 and <=100");
	mPercentage = percentage;
}


/**
 * Gets the discount as percentage.
 * @return the discount as percentage. Guaranteed to be positive and not
 *         higher than 100.0
 */
float getDiscountPercentage() {
	assert (mPercentage >= 0.0) && (mPercentage <= 100.0);
	return mPercentage;
}

So is it possible to remove this redundancy and specify the constraints only once? I think so.
The obvious solution is to add a list of assertions to each argument. This is an attempt at a very compact syntax:

void setDiscountPercentage(float percentage(>=0.0,<=100.0)) {
	mPercentage = percentage;
}

float(>=0.0,<=100.0) getDiscountPercentage() {
	return mPercentage;
}

Each argument is followed by a list of comma-separated constraints that can be checked before executing the function. To make the syntax more crisp it is allowed to start the expressions directly with a comparison operator. They will then be executed with the function argument as first operand. It's also possible to use normal boolean expressions, for instance to check the length of a string:

void setId(String id(!=null, id.length() > 0, id.length() <= 10)) {
	//...
}

The syntax looks pretty cryptic though, because the declaration becomes very long and the nesting of parentheses is confusing. An alternative would be to declare the assertions after the normal signature:

void setDiscountPercentage(float percentage) 
	with percentage: >=0.0, <=100.0 {
	mPercentage = percentage;
}

(float percentage) getDiscountPercentage() 
	with percentage: >=0.0, <=100.0 {
	return mPercentage;
}

void setId(String id) 
	with id: !=null, id.length() > 0, id.length() <= 10 {
	// ...
}

It's a little bit more verbose, and for return value constraints this syntax requires named return values,
but I think it is much more readable than the first attempt. Here an example with more arguments and a return value tuple:

(int a, int b, int c) create3PositiveNumbers(int x)
        with a: >0
	with b: >0
	with c: >0
	with x: >0 {
	return (x, x, x);
}

I think it's quite nice. A few random things:

  • The constraints need to be shown in the API, so they must use only public symbols.
    Otherwise the user would not be able to understand them. The compiler should enforce this.
  • If a reference argument is null, any operation on it will fail. But quite often
    null is allowed as an argument, which would make the constraints quite ugly. Imagine
    a setId() variant that allow nulls:
    void setIdAllowsNull(String id) 
    	with id: (id==null) || (id.length() > 0), (id==null) || (id.length() <= 10) {
    	// ...
    }
    

    An easy solution is the following rule: if an argument is a reference value and it is null, it always passes all tests, unless the first constraint is "!=null". With this rule the constraints can be simplified to

    void setIdAllowsNull(String id) 
    	with id: id.length() > 0, id.length() <= 10 {
    	// ...
    }
    
  • Overridden virtual functions always inherit the constraints and can not add any new constraints (or even change the original ones)
  • Just like regular assert statements it should be possible to disable the argument checks if performance is critical

Comments

Interestimg article! In one of my projects I used a simple template class that did something similar in standard c++ for me.

#include <iostream>
#include <stdexcep>


template<typename T, T low, T high>
class Range
{
public:
    Range(T aValue) : value(aValue)
    {
        if( value < low || value > high )
            throw std::out_of_range("range check error");
    }

    operator T() { return value; }

private:
    T value;
};


void TestFunc(Range<int, 0, 100> percent)
{
    std::cout << percent << std::endl;
}

int main(int argc, char** argv)
{
    TestFunc(5);
    TestFunc(101);
    return 0;
}

I think it's a good solution as long as the C++ doesn't offer something better, because it also nicely documents the allowed range.



Christian



Disclaimer: This was from my memory so there might be coding errors in it.


By Christian Loose at Sat, 02/07/2004 - 11:07

Since you also have an eye on .NET/CLI/C#, I think you might find the following link interesting. It's a presentation made by Herb Sutter. It was held at the last meeting of the C++ standard committee. It's basically talking about how C++ can become a "first class" CLI language.

http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1557.pdf


By Christian Loose at Sat, 02/07/2004 - 11:16

My favorite line is on page 4: "Attempt #1: _ugly_keywords. Users screamed and fled".
Someone should tell this to the Python guys...

I am not sure whether I like the binding though. It's putting a lot of (redundant) features in the language that blow it up quite a bit, and plain C++ is not exactly a small language. If you already know C++ and you want to take advantage of the existing CLR libaries and features it is certainly useful, especially to migrate existing code. But as a stand-alone language I would detest it :)


By tjansen at Sat, 02/07/2004 - 20:14

cool idea!

As usual I got my own syntax. ;)

{RETURN_TYPE|void} {FUNCTION_NAME}( {PARAMETERLIST} )
   [ check( {EXPRESSION} ) ];


if( ! {EXPRESSION} ) {
   throw std::invalid_argument( "{FUNCTION_DECLARATION}: check expression don't matches the function argument(s)" );
}

e.g.:

void SomeClass::setValue( int newValue ) check( ( newValue % 8 ) == 0 ) {
   this->value = newValue;
}

PS: How can I disable the wiki like features in this forum? Or how can I write a '[' so it is displayed as a squared bracket open.


By Mathias Panzenböck at Sat, 02/07/2004 - 20:53

Oh. And how to dissable this?

e.g.:

#ifndef HAS_FUNCTION_ARG_CHECKING
#define check( arg ) /* nothing */
#endif

Um... I think this only works when the syntax is as following:

{RETURN_TYPE|void} {FUNCTION_NAME}( {PARAMETERLIST} )
   [ check(( {EXPRESSION} )) ];

like the gcc extensions for function attributes: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html



Hmm... maybe we should ask the gcc guys to implement someting like an attribute:


check( expr )

When the compiler could eval expr at compiletime, no overhead code will be generated. When !expr at compiletime occurs, the compiler will complain an error. When expr could not be evaled at compiletime, then the compiler will generate code, which cheks the expression at runtime. If this chek fails for c targets the signal SIG_ERR_ARG will be called, for c++ targets std::invaled_argument will be thrown. If SIG_ERR_ARG is not handeld, c targets will stop execution and complain an error on stderr.



Hmmm... or what do you think?





PS: I know, my english is horrible.


By Mathias Panzenböck at Sat, 02/07/2004 - 21:17

void SomeClass::setValue( int newValue ) check( ( newValue % 8 ) == 0 )

IMHO The problem with that is the missing link between the argument and the constraint. The documentation tool would need to be pretty smart, and even then it may fail to find out your intention in some cases (if you use more than one argument in an assertion).


How can I disable the wiki like features in this forum? Or how can I write a ‘[’ so it is displayed as a squared bracket open.

The only way I know is to use HTML escaping with ASCII/Unicode code.


By tjansen at Sat, 02/07/2004 - 22:48

IMHO The problem with that is the missing link between the argument and the constraint.



Um, yes, that's right. But I don't like the "with {PARAMETER_NAME}: <CONSTRAINT>" Syntax.

Maybe this is better:

(<RETURN_TYPE>|"void") <FUNCTION_NAME>"(" <PARAMETERLIST> ")"
   { "check(" <PARAMETER_NAME> ") {" <CONSTRAINT> "}" } ";"



e.g.:

void SomeClass::setValues( int value1, int value2 )
   check( value1 ) { $$ < value2 && $$ >= 0 }
   check( value2 ) { pow( $$, 2 ) <= 1024 }
{
   this->value1 = value1;
   this->value2 = value2;
}

By Mathias Panzenböck at Sun, 02/08/2004 - 12:59

IMHO The problem with that is the missing link between the argument and the constraint.



Um, yes, that's right. But I don't like the "with {PARAMETER_NAME}: <CONSTRAINT>" Syntax.

Maybe this is better:

(<RETURN_TYPE>|"void") <FUNCTION_NAME>"(" <PARAMETERLIST> ")"
   { "check(" <PARAMETER_NAME> ") {" <CONSTRAINT> "}" } ";"



e.g.:

void SomeClass::setValues( int value1, int value2 )
   check( value1 ) { $$ < value2 && $$ >= 0 }
   check( value2 ) { pow( $$, 2 ) <= 1024 }
{
   this->value1 = value1;
   this->value2 = value2;
}

PS: Forgot some HTML escapeing.


By Mathias Panzenböck at Sun, 02/08/2004 - 13:01

You should have a look at the Eiffel language. They have Design by Contract, which consists of pre- and postconditions (and invariants) for your methods. E.g. translated to C++ this would look like:

int MyClass::addMoney(int a)
require (a > 0)
{
 money += a;
 return money;
}
ensure (money = oldval(money) + a)

The require clause must be obeyed by the caller of the method (or an exception is thrown) and the ensure clause promises that the method really does what it is suspected to do. So it is like a contract between caller (=client) and called (=server).

And it is of course also visible in the API documentation so people who use your classes have an exact view of your specifications.

People tried to add this to Java (iContract and such) but it is more a demonstration hack (embedded in comments etc.) which has nowhere the real power of the Eiffel language.

Another approach seems to come from Ada-like languages where you can specify types more exactly than in C-based languages.


By graviton137 at Sat, 02/07/2004 - 21:31

You should have a look at the Eiffel language. They have Design by Contract

Yes, I know, this was basically a follow-up to this entry. I should have linked to it (it is still valid IMHO, but should be used for internal consistency checks only).

I think the problem with the Eiffel syntax is that it a) does not separate internal consistency checks (that may use private members and functions) from the public API contract and b) that there is no connection between the assertions/constraints and the arguments. A message "Your argument 'x' is wrong because it violated the constraint '>0'" is more useful than a long list of assertions, and it also helps for automatic documentation.

An advantage of the Eiffel syntax is that it allows to check not only arguments, but also the method's object. But this is also a disadvantage because it encourages bad design: you could use it to create objects with states that allow you to call methods only under certain circumstances. Sometimes you can't avoid it, but you should avoid it like the plague because it is so error-prone.


By tjansen at Sat, 02/07/2004 - 22:42

Pages