But, back to the janitorial topic, one of the things I'm fond of complaining about VS's code is floats and doubles initialized to integer constants. When browsing the code and seeing foo( 0 ) I tend to assume there's a function overload foo( int ), but after long and fruitless search it turns out there's only foo( float ). Or boolean variables initialized to 0 or 1 instead of false/true...
Also, parameters having special values, like 0.0f or -1.0f to signify special things, like "get the value from somewhere else", makes the code inscrutable to someone who hasn't slept with it since childhood.
Names are also misleading a lot of the time...
Functions that return a value are called "queries", should follow a convention, like start with "get_" and end with the units...
Code: Select all
float get_remaining_strength_cm_durasteel_equiv() const //nothrow()
and ALWAYS be const.
And ideally, const functions should be "pure", that is, not only not change the state of the class, but not change the state of any of the objects passed to them as parameters. That is, when browsing code, just by looking at the name of a function one should be able to tell whether it changes anything at all, or innocently returns a value.
Commands by definition change something, and should return void *always* (use exceptions if necessary; never
return codes) and have names that sound like commands...
Code: Select all
void reset();
void set_fuel_to_percent( float new_fuel_percent_val = 100.0f ) //throw()
{
if( new_fuel_percent_val < 0.0f
|| new_fuel_percent_val > 100.0f ) throw( fuel_percent_out_of_range() );
fuel_percent = new_fuel_percent_val;
}
And variables should never sound like commands, by the same token.
Boolean query functions and variables are the exception; their names should sound like a statement that can be either true or false, like
Code: Select all
bool spec_is_engaged; //or
bool spec::is_engaged() const; //nothrow()
If a float variable should sometimes be marked invalid, rather than use a special value it should be wrapped in a class, and be accompained by a boolean or enum for special cases.
Code: Select all
struct fuel_amount_litres
{
typedef enum { valid, uninitialized, unknown, irrelevant, uses_solar_power } status_t;
status_t status;
float amount; //litres
...
operator float(); //perhaps
};
One of the beauties of adhering to strict command/query separation is that you can infer a lot of stuff about a function just by the way it is used. If a function's return value is not used, it IMPLIES that it returns void, and is a command that changes its class' state. How do you know? Because the value a function returns would simply never NOT be used, because if a function returns a value, that is it's *only* purpose, and wouldn't be called for any other purpose. So, if you see a function whose return is used, you KNOW it is a pure const function that changes nothing. If no return is being used, then you KNOW it is a command that changes something.
When browsing VS code, one basically has no way to know if a function returning a value might have side-effects or not. And often they do, not only within their classes, but affecting things passed to them as pointer or reference parameters. Such functions should be split into separate command and query. Or if a parameter passed to it needs to increment an index at each call, then the whole thing needs to be looked at and refactored, because it's not right that a function should affect any of its parameters, unless it's a clearly labelled output parameter, from which no input is taken. (And even this should be exceptional... Or, not necessarily exceptional, but functions with output parameters don't belong in classes; they should be free functions.)