C++ coding standard suggestion… Separate reasoning from action, in every block. Hi, this talk is to suggest a rule (or guideline) to simplify C++ code.

Slides:



Advertisements
Similar presentations
Test-First Programming. The tests should drive you to write the code, the reason you write code is to get a test to succeed, and you should only write.
Advertisements

Annoucements  Next labs 9 and 10 are paired for everyone. So don’t miss the lab.  There is a review session for the quiz on Monday, November 4, at 8:00.
Debugging Introduction to Computing Science and Programming I.
28-Jun-15 Recursion. 2 Definitions I A recursive definition is a definition in which the thing being defined occurs as part of its own definition Example:
29-Jun-15 Recursion. 2 Definitions I A recursive definition is a definition in which the thing being defined occurs as part of its own definition Example:
C++ Functions. 2 Agenda What is a function? What is a function? Types of C++ functions: Types of C++ functions: Standard functions Standard functions.
Chapter 7 Designing Classes. Class Design When we are developing a piece of software, we want to design the software We don’t want to just sit down and.
A Computer Science Tapestry 1 Recursion (Tapestry 10.1, 10.3) l Recursion is an indispensable technique in a programming language ä Allows many complex.
ICAPRG301A Week 4Buggy Programming ICAPRG301A Apply introductory programming techniques Program Bugs US Navy Admiral Grace Hopper is often credited with.
Current Assignments Homework 2 is available and is due in three days (June 19th). Project 1 due in 6 days (June 23 rd ) Write a binomial root solver using.
Looping and Counting Lecture 3 Hartmut Kaiser
CMP-MX21: Lecture 4 Selections Steve Hordley. Overview 1. The if-else selection in JAVA 2. More useful JAVA operators 4. Other selection constructs in.
1 CS161 Introduction to Computer Science Topic #9.
SEG 4110 – Advanced Software Design and Reengineering Topic T Introduction to Refactoring.
CPSC 217 T03 Week VI Part #1: A2 Post-Mortem and Functions Hubert (Sathaporn) Hu.
Week91 APCS-A: Java Problem Solving November 2, 2005.
Communicating in Code: Commenting Programming Studio Spring 2009 Note: several examples in this lecture taken from The Practice of Programming by Kernighan.
CSC 108H: Introduction to Computer Programming Summer 2012 Marek Janicki.
CSC 108H: Introduction to Computer Programming Summer 2012 Marek Janicki.
CSC 108H: Introduction to Computer Programming
FOP: Multi-Screen Apps
EECS 183.
Developing your Presentation Skills
Introduction to Computing Science and Programming I
Pseudocode and comments
Edward de Bono’s 6 Thinking Hats
Appmarketingminds.com Welcome to App Marketing Minds’ series on how to create viral applications.
Learning to Program D is for Digital.
As the last CC-list represents Maximum Compatible Classes we conclude:
1. Welcome to Software Construction
Testing UW CSE 160 Winter 2017.
Academic representative Committee CHAIR training
© 2015 Pearson Education, Inc., Hoboken, NJ. All rights reserved.
The dirty secrets of objects
Communicating in Code: Commenting
Recursion 12-Nov-18.
Functions Inputs Output
Objects First with Java
Testing UW CSE 160 Winter 2016.
CSCE 315 – Programming Studio, Fall 2017 Tanzir Ahmed
Recursion 2-Dec-18.
Recursion 2-Dec-18.
Functions In Matlab.
Communicating in Code: Commenting
CSCE 489- Problem Solving Programming Strategies Spring 2018
We’re moving on to more recap from other programming languages
Recursion 29-Dec-18.
Variables Title slide variables.
Killer Project Management Best Practices
Algorithms and Problem Solving
Analysis of Algorithms
Recursion Taken from notes by Dr. Neil Moore
CISC101 Reminders All assignments are now posted.
Applied Software Project Management
Bite sized training sessions: CASE tools
Tonga Institute of Higher Education IT 141: Information Systems
Java Programming Loops
Recursion 23-Apr-19.
Impossible problems.
Tonga Institute of Higher Education IT 141: Information Systems
Analysis of Algorithms
Pseudocode and comments
CMSC 202 Exceptions.
Software Development Techniques
CMSC201 Computer Science I for Majors Lecture 12 – Program Design
Analysis of Algorithms
The Taxidermy Studio of Alfred Giles
Presentation transcript:

C++ coding standard suggestion… Separate reasoning from action, in every block. Hi, this talk is to suggest a rule (or guideline) to simplify C++ code – separate reasoning from action, in every block. I don’t see people doing this, probably because the result goes against our instincts of what good code looks like, but I want to argue its good points. I hope that thinking about the consequences of the rule will be useful, and I welcome feedback.   In 20 years working as a programmer, the most serious problem I see, which occurs in every codebase I’ve worked with, is that code gets too complicated over time. There are many aspects to this, but I’m going to talk about how small amounts of code can get complicated. Lets take the example of a class member function that calculates fibonacci numbers. Jeff Hannan

Fibonacci v.1 The function calculates the nth fibonacci number.   Even if the function is written in a really clear way to start with,

Fibonacci v.2 Once it is modified with bug fixes

Fibonacci v.3 and additional features, it will become more complex.

Fibonacci v.4 I’ve added a lot to the function here just to gather statistics, and the result is not good.   A function with lots of changes tends to become crammed with logic, making it unwieldy and difficult to understand. This member function now has several outputs, and they are written to throughout the function. Refactoring is a good way to deal with this. But the reality is that for many of us, despite best efforts, code degenerates over time. I work with great programmers, so it is not due to bad coding. We just can’t refactor fast enough. However, coding standards have to be followed for all new code, so they are an effective way to keep code quality high, in the face of changes.

Coding standards that reduce complexity have a big impact. So, coding standards that reduce complexity have a big impact.

The rule reasoning statements action statements Divide up the statements in a block into 2 parts, in the following order: reasoning statements how the output is calculated action statements what the program is doing The rule then, is one that can be applied at all times, to most types of function.   It is to divide up the statements in a block into 2 parts, in the following order: reasoning statements action statements The reasoning statements do not write any outputs of the block. It is the const part of the block. Data gathering and working out, all within block scope. The action statements just write the outputs of the block. It simplifies because it divides up the code into two parts, each of which you only look at for a different reason. To understand what the program is doing, we only need to look at the action - the output that is written. To understand how the output is calculated, we only need to look at the reasoning.

If you look at some code, I expect you will find that these expressions and statements are all intermingled. A typical example is if (newX != oldX) as in { ProcessNewX(newX); } That one line contains 2 pieces of logic, that can be separated. const bool xHasChanged = ( newX != oldX ); if (xHasChanged) I know that many programmers will look at that, and think it is unnecessary overhead. How is this simplifying things, you rightly ask? Well, the line is simpler than the line The reasoning for how x has changed has been moved up. If we are just looking at what the program is doing, we don’t need to know how xHasChanged is calculated. Of course we could do this: if (HasXChanged(newX)) and I accept that is pretty clear, but it still isn’t as simple as checking a boolean.   This approach will lead to an increase in temporary variables. And too many temporaries are seen as a bad thing, because there are more variables to think about. But though temporary variables increase the number of variables in scope, they do allow us to discard used data. However much information is used to determine if x has changed, it is boiled down to a boolean. When it comes to the Action, you can mentally discard the reasoning.

Temporary variables can reduce the number of inputs into the Action code. Temporary variables can therefore reduce the number of inputs into the Action code.

Fibonacci v.4 inputs and outputs Function inputs: n Function outputs: m_numberOfCalls m_lastResult m_totalSum next total Loop block inputs: n first second total m_totalSum Loop block outputs: next else block inputs: n else block outputs: total m_totalSum next One of the great causes of complexity is having too many inputs and outputs. A function transforms inputs into outputs. But when there are several, this becomes a very complicated tranformation, made worse when there are temporary variables, and access to member variables and object data. A block has access to the variables in its outer scope. So, a small piece of code can have access to a large amount of data.   This can be seen in the example function. The function itself has 1 input and 5 outputs. The else block has a smaller number of outputs, but the loop block has 5 outputs, some of which are also function outputs. This code is getting complicated, and means that someone reading the code has to unpick it. Especially if they have to fix a bug or make further modifications. When I see code that has many inputs and outputs, I wonder if the authors understood the problem space, and the mapping that is being performed. When code has been added to over time, like this one, probably not. Sometimes, I’m the author. Without refactoring into functions, we can’t control all the potential inputs and outputs. But, we could have written the code in such a way that minimises the number of inputs and outputs at each stage.

Block input/output pipeline Block Inputs Block Inputs Subset of data accessible to block Don’t write data that is outside block scope Reasoning code Block code Calculate block outputs Action Inputs Minimise these Make this as simple as practically possible Action code Block Outputs Subset of data accessible to block and/or function return value Consider that each block is an input/output pipeline. The block code uses a subset of all the data it has access to, and transforms the block inputs into the outputs.   By separating the block code into Reasoning and Action, we can say that the Reasoning code does some calculation and produces output. But must write no data that is outside block scope. The Action code, takes the minimal subset of inputs. We should make this as simple as is practically possible. Moving anything that can be pre-calculated up into the Reasoning code. The action code is what changes the data, and it should be easy to see how that happens. I anticipate that a big question is – can you really divide up the logic of a block like that? Well, I’ve looked at a lot of code and found that typically you can, but not always. There are varying degrees at which you can apply the idea, and I’m trying to take it as far as I can. Block Outputs

Fibonacci v.4 using rule Here is how the function is written following the rule.   The result is long-winded. It goes against my instincts, by using unnecessary temporary variables, and bloating the functions. But it is much easier to see, what each block does. All the output is written at the end of the function, so it is easy to see what the function does. Each of the 3 blocks in the if statement ends by writing out the same 3 variables. And the loop updates and writes out 4 variables. I’ve commented the Reasoning and Action parts. Essentially, each block is self contained, and like a function in itself. So, a function with nested blocks can be understood more easily, because the outputs of any block should be obvious. And it should be easy to see where the outputs of the function are set. My conclusion is that in practice, the rule is beneficial most of the time. In this example, the Action only writes output directly. But, the Action can call other functions, use if statements and loops, as long as those write the outputs of the block.

Summary Code is simpler Coding standard to resist complexity Not refactoring, but helps refactoring Oversimplifies deliberately I find that this rule makes it easier to read and understand code. That code is simpler when you split the reasoning (or working out) from the action. And by making this a coding standard, it is one way to resist code becoming too complex.   This rule is for writing code, so it is not refactoring. But it does make refactoring easier, because each block is already organised like a function, with explicit outputs. And, while this isn’t an ideal way of writing code, because it is oversimplifying it, it prepares the code for future changes. I will finish with the full rule, as I apply it. I hope you’ll give it some thought, thank you.

The rule as I use it The rule applies hierarchically, to the statements in each block, like this: int FunctionName() { // Reasoning // Action } Reasoning - can contain sub-blocks and call functions. Defining and setting variables with block scope is done here. Action - can contain sub-blocks and call functions, but only if they write the outputs of the block. i.e. you can pass a reference to a function which writes to it. Set outputs, or update outputs using an operator e.g. m_totalSum += totalSumDelta. Asserts can be anywhere and are not covered by the rule.