260 likes | 346 Views
Subroutine Design and Spartan Programming. Size Indentation Style Comments Variables Operator Overloading. Rule I. Enable ALL compiler warnings; understand ALL compiler warnings and error messages; coerce program to compile cleanly without any warning
E N D
Subroutine Design and Spartan Programming • Size • Indentation • Style • Comments • Variables • Operator Overloading sd
Rule I • Enable ALL compiler warnings; understand ALL compiler warnings and error messages; coerce program to compile cleanly without any warning • Never, Never, Never circumvent a warning • Rationale: • Although not all warnings are potential bugs, all ignored warnings have the potential of crucial important warnings. • Effort Equation: a moment invested in compilation is an hour earned in debugging. • Reason for Caution: there now are more ex-Pascal (ex-COBOL) C++ programmers than ever before! sd
Rule II • Function Area: less than a hand2 • Vertical Hand Span Law: no function should be longer than one hand span. • Modular comprehension • Evenly spread documentation- • line or two / function • Meaningful naming • Vertical Hand Span Law: no function should be deeper than one hand span. • Nested control statements are hard to read and understand sd
Reducing Function Depth • Eliminate exceptions and error conditions early • Use return (throw in C++) • Use return, break and continue sparingly (and wisely) • Do not write else after return (break, continue) • The shortest branch should always be first char *strcpy(char *dst, char *src){if (src == (char *)0 || dst == (char *)0)return src; ... } if (src == (char *)0 || dst == (char *)0)return src;else { // unnecessarily deep block } sd
Reducing Function Depth • Eliminate exceptions and error conditions early • Use return (throw in C++) • Use return, break and continue sparingly (and wisely) • Do not write else after return (break, continue) • The shortest branch should always be first char *strcpy(char *dst, char *src){if (src == (char *)0 || dst == (char *)0)return src; ... } if (src == (char *)0 || dst == (char *)0)return src;else { // unnecessarily deep block } sd
Input and Output Arguments • Three Kinds of Arguments • Input • Output • Input and Output • Output Arguments: not common in good design. • If there is only one returned value, then the method could be made into a function that returns this value. • Two or more returned values are less common. They are usually an indication of bad design. • Input/Output Arguments: very rare. • Danger Signal: An argument which serves as input to a method , and is also changed by the same method, brings the suspicion: • Maybe the method should have been sent to this argument? • Typical case: • Several inputs (almost always less than 4) • 0/1 outputs. sd
Const Correctness • A program is constcorrect if and only if: • All immutable elements are declared as such (maximal number of cons objects). • No immutable element is mutated, or equivalently, the program compiles without any mutability related warning or error messages. used with global variables, arguments to functions, functions return values, internal variables, data members, inspectors, etc. • Type safety requires you to annotate your code with type information which isn't absolutely necessary. • You, as a programmer, know a lot of important information about your program. Type safety and const correctness are structured ways to tell the compiler about this information. • In return you get: • More robust systems. • Fewer run-time errors. • More efficient programs. “const correctness” (just like type-checking) can be time consuming and tedious! sd
Indentation • Very important: use visual appearance as means for transferring information on structure. • Preferred Style: Kernighan & Ritchie - • The most common indentation style • Wisdom not always apparent to novices: • Compound control statements control statement are different than nested blocks • Efficient usage of precious screen/page lines • else cuddling for (…) {…} for (;;);{…} “Pascal” Style for (…) { …} for (;;) ;{…} K&R Style sd
Appeal to Reader’s Experience • Good: • if (f(exp)) … • return exp; • a + b*c; • class Derived: public Base { • Appeal to mathematics background; functions and control are distinct; use spaces to emphasize precedence • Bad: • if( f (exp))… • return(exp); • a+b * c; • class Derived : public Base { sd
Primitive Types: int, char, … • int: should be used very rarely - for values for which size is of no importance. • Array indices: use size_t • Loops: size_t if they are for arrays • Registers and bit masks: use int16 and int32 • char: for characters only • Use wchar_t for Unicode • typedef for bytes • unsigned: a recipe for trouble; avoid if not absolutely necessary sd
Structs and Unions struct, union, array, pointer • Avoid unions: they tend to be low level and error prone. • Use struct for C’s classes only: apply C++’s keyword redundancy as leverage, not as a tool for creative diversity • Avoid arrays: arrays are error prone. • use STL container classes instead • Avoid pointer arithmetic's: more dangerous than arrays • use STL container classes instead • Avoid loops • use STL container classes instead sd
NIL Pointer #define NIL(Type) (Type *)(0) • 0 is: • A Null pointer of all types • An octal integer • A character • A floating point number • Might lead to confusion and errors • By using an explicit type for each 0, the risk of confusion and errors is reduced extern f(int *);extern f(char); f(0); // which f is called here? sd
Variables Variables won’t; constants are not! (Murphy) • Functional Programming (Lisp): variable-less programming is possible. • Spartan C: hunt down variables! • Minimize number • Minimize life time • Minimize habitat • Minimize variability sd
Minimize Number of Variables • Goal: 3-4 variables / function at the most except for rare cases. Much fewer with data function members • Means: • Unite into structure and unions • Code inspection • Function splitting sd
Minimize Variables Life Time • Life-time ladder • None • Function call • First call till end of program: function static variables • Program lifetime: global variables • Programmer defined: heap variables sd
Restrict Variables Scope • Scope Ladder: • Control statement: (if/for/while) • Just like functional programming let statement • Statement block • Function • File • Program • Application for (int i = 0; i < N; i++)for (int j = i; j < i; j++);… if (a[i] < a[j]) {int tmp = a[i]; a[i] = a[j]; a[j] = tmp;} sd
Minimize Variability • Studies have shown that 40% of all variables in ordinary C programming are really constant- they are assigned once and never changed later. • Express as const • Percentage of consts can be enlarged by introducing new variables to express temporary quantities • Just like let command in functional programming sd
Expressions • Minimize expression complexity • Size: number of leaves in expression tree • Players: number of distinct leaves • Ideally, no variable should appear more than once. • Use compound advanced assignment operators to reduce number • No literal constant should appear more than once in the code • Abstract using enums/const variables to express commonality among literal constants. • Never put numerical constants in the code other than the values 0, 1, and -1. • Weird Operators: • Never: bitwise operators (no one remember their precedence) • Avoid: ?: • Side effects: 0 for ordinary expressions/ 1 for assignments / 2 or more only in special cases while (*t++ = *s++) ; sd
Boolean Type #ifdef BOOL#elsetypedefint bool;#define true 1#define false 0#endif • LOOK FOR THE FUTURE • Follow the standard development • Strive to use the more advanced feature • Apply macros as guard against obsolete compilers sd
Comments • Exploit visual distinctions to aid reader • C++ comments: one liners • C comments: comment blocks for software units • Documentation is like spices: should be put at the right level sd
Variable Declarations • Avoid mixing declarations with statements. • If you constantly find yourself required to introduce variables just before they are used, then your design is lacking: • Split function • Restructure • Constant variables are OK though. sd
Function Overloading • Avoid if possible! Use in great care, and only in cases it is absolutely necessary • Selecting a function among a bunch of overloaded ones is one of the most delicate and complicated issues of C++. • Issues involved: • templates • const/volatile • throw list • inheritance • built-in type casts • user defined type casts • default values • … • and others ... • Operator Overloading: don’t use. Arithmetical types should come from library. sd
Domain of Classes and Operator Overloading • There is a ladder of domains from which classes are taken. • Effort should be concentrated on mid-range classes! These rarely have operator overloading. • Domains are characterized by the extent to which its classes are applicable: • Problem Space • Application Domain • One program or application. • Business Domain • One industry or company. • Program Space • Architectural Domain • One implementation architecture. • Foundation Domain • Across all businesses and architectures. [Meilir Page-Jones, What Every Programmer Should Know about OOD, Chapter 9, Dorest ‘95] Low Reusability MediumReusability High Reusability sd
Exceptions #ifdef EXCEPTIONS#define THROW_LIST(x) throw x#define THROW(x) throw x#define RETHROW(x) throw#else#define THROW_LIST(x) #define THROW(x) terminate(WHERE,#x) #define RETHROW(x) throw #endif sd
New Cast Operators #ifdef NEW_CASTS #define STATIC_CAST(T) static_cast<T> #define DYNAMIC_CAST(T) dynamic_cast<T> #define CONST_CAST(T) const_cast<T> #define REINTERPRET_CAST(T) reinterpret_cast<T> #else #define STATIC_CAST(T) (T) #define DYNAMIC_CAST(T) (T) #define CONST_CAST(T) (T) #define REINTERPRET_CAST(T) (T) #endif #define IGNORE STATIC_CAST(void) CAST IN STONE: Avoid Dangerous C Style Casts Discard (if possible) unused returned values sd
Avoid Macros #define BUFF_SIZE 1024 #define NL '\n' #define PI 3.1415926 #define TWICE(x) ((x)<<1) #define SQR(x) ((x)*(x)) enum { BUFF_SIZE = 1024 }; enum { CR = '\r', LF = '\n' }; const double Pi = 3.1415926; inline twice(int x) { return x << 1; } template <class Type> Type sqr(Type x) { return x * x; } C macros are a primitive language construct, which does not respect other constructs such as name space, encapsulation, etc. It should be used only when it is absolutely necessary- mostly for meta-programming but also for including filesSome techniques for avoiding macros are illustrated below: sd