CppFunctionDesign.md
January 10, 2018 ยท View on GitHub
Function design is the correct design of a function.
Advice
General
- Avoid writing long functions [1-2,5,35]
- Avoid using functions with a large number of arguments [36]
- Prefer functions over macros [23,24]
- If a function may have to be evaluated at compile time, declare it constexpr [7]
The name of the function should say what it does
- Do use names like Swap, IsPrime, StrToInt
- Do not use names like
DoIt,Transmogrify,Stuff - Exceptions:
Transmogrifyis a function name commonly used in the literature to denote a function you are not aware of what it is doing
A function should perform a single logical operation [4]
- Do not use names with 'And' in it, like SwapAndGetMean
- Do use two functions instead, Swap and GetMean
- Exceptions: some mathematical functions can cooperate and so improve runtime speed: MeanAndStdDev executes faster then calling the seperate functions for Mean and StdDev
Interface
- Make interfaces easy to use correctly and hard to use incorrectly [41,47]
- Make interfaces explicit (that is, among others, independent of global variables) [46]
- Seperate the interface of a class from its implementation [44]: allow the user not to know what kind of data types you used in the private section of your class
- Use const pointers and const references to express immutability in interfaces [43]
- Document the interfaces so that they are usable by others [42]
- Have at least one other developer review each interface [42]
- Prefer public members for interfaces [45]
The return type is expected from the name of the function
- If a function cannot return, mark it [[noreturn]] [8]
- Do give a function a void return type when it has no return type
- Do return a non-pointer non-reference object when a function is expected to always execute successfully on the assumptions that its arguments are valid
- Return a result as a return value rather than modifying an object through an argument [11]
- Do return a pointer (or better: a smart pointer) to a data type when the return type can be null, that is: empty. If the pointer needs to be read-only, make it const (for example 'const T * const GetT()', where GetT is a function that returns a pointer to a T)
- Do not make a function return a reference to a function's local variable [6,31], this leads to undefined behavior
- Do not make a function return an error code, use exceptions instead [32]
The function arguments are expected from the name of the function
- A function declared with an empty parameter list takes no arguments [37]. Or: write 'int f()' instead of 'int f(void)' [37]
- Prefer pass-by-reference-to-const to pass-by-value [30]
- Use pass-by-non-const-reference only if you have to [14]
- Do use a non-pointer reference object for expensive-to-copy data types, like std::string, std::vector<int> or Database. Make the argument const if it must be marked read-only [10]
- Do use a non-pointer non-reference object for standard data types like int [9]. Make the argument const if it must be marked read-only
- Do use a pointer (or better: a smart pointer) to a data type when the argument can be nullptr/null/empty [13]. If the pointer needs to be read-only, make it const (for example 'void CoutT(const T * const t)', where CoutT is a function that uses std::cout on a T)
- Avoid passing arrays as pointers [16]
- Assume that a char* or a const char* argument points to a C-style string [15]
- Use rvalue references to implement move and forwarding [12]
- Pass a homogeneous list of unknown length as an std::initializer_list or as some other container [17]
- Avoid unspecified numbers of arguments (...) [18]
- Prefer to pass function objects (including lambdas) and virtual functions to pointers to functions [22]
Specify preconditions and postconditions for your functions [21,25-28,38,40]
- Do use Expects (preferred [39]) and exceptions to make clear to the client which input the function cannot handle. For example, the square root of a negative number does not exist
- Prefer Ensures to express postconditions [48]
- Do not use error codes as return types, use exceptions instead [32]
Exception handling
- Don't try to catch every exception in every function [29]
- Assume that every exception that can be thrown by a function will be thrown [33]
- If a function may not throw, declare it noexcept [34]
Function overloading
- Use overloading when functions perform conceptually the same task on different types [19]
- When overloading on integers, provide functions to eliminate common ambiguities [20]
See also
- Exercise #2: correct function declarations is an exercise in correct function design.
References
- [1] Herb Sutter, Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices. ISBN: 0-32-111358-6. Item 20: 'Avoid long functions. Avoid deep nesting'
- [2] Joint Strike Fighter Air Vehicle C++ Coding Standards for the System Development and Demonstration Program. Document Number 2RDU00001 Rev C. December 2005. AV Rule 1: 'Any one function (or method) will contain no more than 200 logical source lines of code.'
- [3] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[1] "Package meaningful operations as carefully named functions'
- [4] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[2] A function should perform a single logical operation'
- [5] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[3] Keep functions short'
- [6] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[4] Don't return pointers or references to local variables'
- [7] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[5] If a function may have to be evaluated at compile time, declare it constexpr'
- [8] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[6] If a function cannot return, mark it [[noreturn]]'
- [9] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[7] Use pass-by-value for small objects'
- [10] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[8] Use pass-by-const-reference to pass large values that you don't need to modify'
- [11] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[9] Return a result as a return value rather than modifying an object through an argument'
- [12] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[10] Use rvalue references to implement move and forwarding'
- [13] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[11] Pass a pointer if "no object" is a valid alternative (and represent "no object" by nullptr)'
- [14] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[12] Use pass-by-non-const-reference only if you have to'
- [15] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[14] Assume that a char* or a const char* argument points to a C-style string'
- [16] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[15] Avoid passing arrays as pointers'
- [17] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[16] Pass a homogeneous list of unknown length as an initializer_list (or as some other container)'
- [18] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[17] Avoid unspecified numbers of arguments (...)'
- [19] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[18] Use overloading when functions perform conceptually the same task on different types'
- [20] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[19] When overloading on integers, provide functions to eliminate common ambiguities'
- [21] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[20] Specify preconditions and postconditions for your functions'
- [22] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[21] Prefer function objects (including lambdas) and virtual functions to pointers to functions'
- [23] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 12.7. Advice. page 341: '[22] Avoid macros'
- [24] Herb Sutter, Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices. 2005. ISBN: 0-32-111358-6. Item 16: 'Avoid macro's'.
- [25] Herb Sutter, Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices. ISBN: 0-32-111358-6. Chapter 68: 'Assert liberally to document internal assumptions and invariants'
- [26] Bjarne Stroustrup. The C++ Programming Language (3rd edition). 1997. ISBN: 0-201-88954-4. Advice 24.5.18: 'Explicitly express preconditions, postconditions, and other assertions as assertions'
- [27] Steve McConnell. Code Complete (2nd edition). 2004. ISBN: -735619670. Chapter 8.2 'Assertions', paragraph 'Guidelines for using asserts': 'Use assertions to document and verify preconditions and postconditions'
- [28] Steve McConnell. Code Complete (2nd edition). 2004. ISBN: -735619670. Chapter 8.2 'Assertions', paragraph 'Guidelines for using asserts': 'Use assertions for conditions that should never occur'.
- [29] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 13.7. Advice. page 387: '[8] 'Don't try to catch every exception in every function'
- [30] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6. Item 20: Prefer pass-by-reference-to-const to pass-by-value.
- [31] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6. Item 21: Don't try to return a reference when you must return an object.
- [32] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 13.7. Advice. page 386: '[3] Use exceptions for error handling'
- [33] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 13.7. Advice. page 387: '[33] Assume that every exception that can be thrown by a function will be thrown'
- [34] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 13.7. Advice, page 387: '[23] If your function may not throw, declare it noexcept'
- [35] Bruce Eckel. Thinking in C++, second edition, volume 1. 2000. ISBN: 0-13-979809-9. Chapter B: Programming Guidelines. Item 15: 'Watch for long member function definitions. A function that is long and complicated is difficult and expensive to maintain, and is probably trying to do too much all by itself. If you see such a function, it indicates that, at the least, it should be broken up into multiple functions. It may also suggest the creation of a new class.'
- [36] Bruce Eckel. Thinking in C++, second edition, volume 1. 2000. ISBN: 0-13-979809-9. Chapter B: Programming Guidelines. Item 16: 'Watch for long argument lists. Function calls then become difficult to write, read and maintain. Instead, try to move the member function to a class where it is (more) appropriate, and/or pass objects in as arguments.'
- [37] Working Draft, Standard for Programming Language C++. 2014-08-22. N3936. Paragraph C.1.7. First change. 'In C++, a function declared with an empty parameter list takes no arguments. In C, an empty parameter list means that the number and type of the function arguments are unknown.'
- [38] C++ Core Guidelines: I.5: State preconditions (if any)
- [39] C++ Core Guidelines: I.6: Prefer Expects() for expressing preconditions
- [40] C++ Core Guidelines: I.7: State postconditions
- [41] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6. Item 18: Make interfaces easy to use correctly and hard to use incorrectly.
- [42] John Lakos. Large-Scale C++ Software Design. 1996. ISBN: 0-201-63362-0. Chapter 2.6: Document the interfaces so that they are usable by others. Have at least one other developer review each interface
- [43] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 7.8. Advice. page 199: '[13] Use const pointers and const references to express immutability in interfaces'
- [44] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 16.4. Advice. page 479: '[2] Seperate the interface of a class from its implementation'
- [45] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 20.7. Advice. page 611: '[11] Prefer public members for interfaces'
- [46] C++ Core Guidelines: I.1: Make interfaces explicit
- [47] C++ Core Guidelines: I.4: Make interfaces precisely and strongly typed
- [48] C++ Core Guidelines: I.8: Prefer Ensures() for expressing postconditions