Skip to the content of the web site.

Lesson 1.23: Code-development strategies

Previous lesson Next lesson


For small projects, it is often tempting to get it done. That is, open the IDE, start coding, and the sooner you finish, the better you feel.

Serious software development doesn't happen like this: Instead, it is always important to first write what it is you are trying to accomplish in English. This may seem unnecessary; after all, if you can describe the algorithm in C++ or the language of your choice, why actually bother doing anything else?

The issue is that if you cannot write the algorithm down in English, chances are you're guessing at certain parts: you know what you want to do, but you're not sure exactly what it is you're doing; i.e., it just works.

Right now, you can start: you would like to write ten functions that return the volumes and surface areas of the five Platonic solids: the

  1. tetrahedron,
  2. cube,
  3. octahedron,
  4. dodecahedron, and
  5. icosahedron.

First, you have to answer: how is it you want to have the user describe these solids: reading through the literature, you note that one common characteristic among all of the solids is that there is a formula relating the volume and surface area relative to the length of any one edge. (After all, the five Platonic solids are the only five solids that equal angles and equal sides.)

We can start with a short description and declaring each of the functions:

#include <iostream>

////////////////////////////////////////////////////////////////
// Volumes and surface areas of the five Platonic solides
//  - This collection of functions will return the volumes
//    and surface areas of the five Platonic solids:
//                       Faces   Shape     Vertices  Edges
//        tetrahedron      4    triangle      4        6
//        cube             6    square        8       12
//        octahedron       8    triangle      6       12
//        dodecahedron    12    pentagon     20       30
//        icosahedron     20    triangle     12       30
////////////////////////////////////////////////////////////////

// Function declarations
int main();
double tetrahedron_volume( double side );
double cube_volume( double side );
double octahedron_volume( double side );
double dodecahedron_volume( double side );
double icosahedron_volume( double side );

double tetrahedron_area( double side );
double cube_area( double side );
double octahedron_area( double side );
double dodecahedron_area( double side );
double icosahedron_area( double side );

// Function definitions

int main() {
	return 0;
}

You can look at this code at repl.it.

If you compile this, you will see that it works perfectly: there are a few functions that are declared, but because they are never used, the compiler ignores them. This is a fully functioning program that does nothing. What's important here, however, is you now have an idea as to what you must do, and at the very least, you have not made any mistakes up to this point. If you had used an incorrect function declaration, the compiler would quickly let you know.

Now, if you try to modify main() to actually call one of your functions:

int main() {
	std::cout << "The volume of an octahedron with a side length "
	          << 1.573 << " is " << octahedron_volume( 1.573 ) << std::endl;

	return 0;
}

The problem is that this doesn't even compile: the function definition for octahedron_volume(...) is missing. The error message is also a little confusing:

/tmp/ccXLT6YF.o: In function 'main':
q.cpp:(.text+0x5a): undefined reference to 'octahedron_volume(double)'
collect2: error: ld returned 1 exit status

Notice that it does not give a line number. This is because this error message is created during the linking process: the compiler is trying to find the definition of the octahedron_volume function, and it cannot find it.

Your initial reaction may be to 'just start coding' the function definitions, but that is never-the-less wrong.

Begin with default definitions and tests

First, give each function a comment block and a default return value. The default value for a double is 0.0, so have every function return that value.

Next, write a main function that tests each function call. In this case, the author used a 12-digit-display calculator to calculate each of the formulas using a side equal to 2.

  //////////////////////////
 // Function definitions //
//////////////////////////

int main() {
	std::cout << "The volume of a tetrahedron with a side length "
	          << 2.0 << " is " << tetrahedron_volume( 2.0 )
		  << std::endl;
	std::cout << " - expecting 0.942809041582" << std::endl;

	std::cout << "The volume of a cube with a side length "
	          << 2.0 << " is " << cube_volume( 2.0 )
		  << std::endl;
	std::cout << " - expecting 8" << std::endl;

	std::cout << "The volume of an octahedron with a side length "
	          << 2.0 << " is " << octahedron_volume( 2.0 )
		  << std::endl;
	std::cout << " - expecting 3.77123616633" << std::endl;

	std::cout << "The volume of a dodecahedron with a side length "
	          << 2.0 << " is " << dodecahedron_volume( 2.0 )
		  << std::endl;
	std::cout << " - expecting 61.3049516850" << std::endl;

	std::cout << "The volume of an icosahedron with a side length "
	          << 2.0 << " is " << icosahedron_volume( 2.0 )
		  << std::endl;
	std::cout << " - expecting 17.4535599250" << std::endl;


	std::cout << "The surface area of a tetrahedron with a side length "
	          << 2.0 << " is " << tetrahedron_area( 2.0 )
		  << std::endl;
	std::cout << " - expecting 6.92820323028" << std::endl;

	std::cout << "The surface area of a cube with a side length "
	          << 2.0 << " is " << cube_area( 2.0 )
		  << std::endl;
	std::cout << " - expecting 24" << std::endl;

	std::cout << "The surface area of an octahedron with a side length "
	          << 2.0 << " is " << octahedron_area( 2.0 )
		  << std::endl;
	std::cout << " - expecting 13.8564064606" << std::endl;

	std::cout << "The surface area of a dodecahedron with a side length "
	          << 2.0 << " is " << dodecahedron_area( 2.0 )
		  << std::endl;
	std::cout << " - expecting 82.5829152283" << std::endl;

	std::cout << "The surface area of an icosahedron with a side length "
	          << 2.0 << " is " << icosahedron_area( 2.0 )
		  << std::endl;
	std::cout << " - expecting 34.6410161514" << std::endl;

	return 0;
}

//////////////////////////////////////////////////////////
// Volume of a tetrahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the tetrahedron
// @return     the volume of the tetrahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is cubic inches
//              - if the user passes cm,
//                      the result is cm^3
//
// @section FORMULA
//   If 's' is the length of a side:
//                3 /    _
//               s / 6 \/2
//////////////////////////////////////////////////////////

double tetrahedron_volume( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Volume of a cube
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the cube
// @return     the volume of the cube
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is cubic inches
//              - if the user passes cm,
//                      the result is cm^3
//
// @section FORMULA
//   If 's' is the length of a side:
//                3
//               s
//////////////////////////////////////////////////////////

double cube_volume( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Volume of a octahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the octahedron
// @return     the volume of the octahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is cubic inches
//              - if the user passes cm,
//                      the result is cm^3
//
// @section FORMULA
//   If 's' is the length of a side:
//               1   _  3
//               - \/2 s
//               3
//////////////////////////////////////////////////////////

double octahedron_volume( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Volume of a dodecahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the dodecahedron
// @return     the volume of the dodecahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is cubic inches
//              - if the user passes cm,
//                      the result is cm^3
//
// @section FORMULA
//               1          _   3
//               - (15 + 7\/5) s
//               4
//////////////////////////////////////////////////////////

double dodecahedron_volume( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Volume of a icosahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the icosahedron
// @return     the volume of the icosahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is cubic inches
//              - if the user passes cm,
//                      the result is cm^3
//
// @section FORMULA
//               5        _   3
//              -- (3 + \/5) s
//              12
//////////////////////////////////////////////////////////

double icosahedron_volume( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Surface area of a tetrahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the tetrahedron
// @return     the surface area of the tetrahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is square inches
//              - if the user passes cm,
//                      the result is cm^2
//
// @section FORMULA
//   If 's' is the length of a side:
//                     _  2
//                   \/3 s
//////////////////////////////////////////////////////////

double tetrahedron_area( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Surface area of a cube
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the cube
// @return     the surface area of the cube
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is square inches
//              - if the user passes cm,
//                      the result is cm^2
//
// @section FORMULA
//   If 's' is the length of a side:
//                2
//             6 s
//////////////////////////////////////////////////////////

double cube_area( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Surface area of a octahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the octahedron
// @return     the surface area of the octahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is square inches
//              - if the user passes cm,
//                      the result is cm^2
//
// @section FORMULA
//   If 's' is the length of a side:
//                   _  2
//               2 \/3 s
//////////////////////////////////////////////////////////

double octahedron_area( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Surface area of a dodecahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the dodecahedron
// @return     the surface area of the dodecahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is square inches
//              - if the user passes cm,
//                      the result is cm^2
//
// @section FORMULA
//                    ____________
//                   /          _   2
//               3 \/ 25 + 10 \/5  s
//////////////////////////////////////////////////////////

double dodecahedron_area( double side ) {
	return 0.0;
}

//////////////////////////////////////////////////////////
// Surface area of a icosahedron
//
// @author    Douglas Wilhelm Harder
// @date      2018-09-17
// @version   1.0
//
// @param side the length of a side of the icosahedron
// @return     the surface area of the icosahedron
//              - the units of the return value is the cube
//                of the units of whatever the user passed
//              - if the user passes inches,
//                      the result is square inches
//              - if the user passes cm,
//                      the result is cm^2
//
// @section FORMULA
//                  _   2
//              5 \/3) s
//////////////////////////////////////////////////////////

double icosahedron_area( double side ) {
	return 0.0;
}

You can look at this skeleton at repl.it.

Now, you're wondering what the point is. It is this: this will compile, even though every function returns completely the wrong answer (unless $s = 0$).

The volume of a tetrahedron with a side length 2 is 0
 - expecting 0.942809041582
The volume of a cube with a side length 2 is 0
 - expecting 8
The volume of an octahedron with a side length 2 is 0
 - expecting 3.77123616633
The volume of a dodecahedron with a side length 2 is 0
 - expecting 61.3049516850
The volume of an icosahedron with a side length 2 is 0
 - expecting 17.4535599250
The surface area of a tetrahedron with a side length 2 is 0
 - expecting 6.92820323028
The surface area of a cube with a side length 2 is 0
 - expecting 24
The surface area of an octahedron with a side length 2 is 0
 - expecting 13.8564064606
The surface area of a dodecahedron with a side length 2 is 0
 - expecting 82.5829152283
The surface area of an icosahedron with a side length 2 is 0
 - expecting 34.6410161514

Now you can implement one function at a time and compile it. If the function works, you can go to the next function; however, if there is a problem, you know exactly that the function in question is the one that broke.

For example, you may author the first function, tetrahedron_volume(...) apparently, correctly:

double tetrahedron_volume( double side ) {
	return s^3/6.0/sqrt(2.0);
}

However, you compile it:

example.cpp: In function 'double tetrahedron_volume(double)'
example.cpp:75:16: error: 's' was not declared in this scope
         return s^3/6.0/std::sqrt(2.0);
                ^

example.cpp: In function 'double tetrahedron_volume()':
example.cpp:75:16: error: 'sqrt' was not declared in this scope:
	return s^3/6.0/sqrt(2.0);
                       ^

You note you forgot to use side and you remember that sqrt is in the std namespace, so you prefix it with std:::

double tetrahedron_volume( double side ) {
	return side^3/6.0/std::sqrt(2.0);
}

You compile again, only to get another message:

example.cpp: In function 'double tetrahedron_volume()':
example.cpp:75:16: error: 'sqrt' is not a member of 'std':
	return s^3/6.0/std::sqrt(2.0);
                       ^

At this point, you determine that you need the math library, so just below the inclusion of iostream, you add:

#include <iostream>
#include <cmath>

The "c" in cmath indicates that this is—more-or-less—the mathematics library from the C programming lanague.

You compile yet again: it should work now...

example.cpp: In function 'double tetrahedron_volume(double)'
example.cpp:75:40: error: invalid operands of types 'double' and 'double'
to binary 'operator^'
	return side^3/6.0/std::sqrt(2.0);
                                       ^

Looking the caret operator in C++, you realize that ^ is not exponentiation, so you fix it:

double tetrahedron_volume( double side ) {
	return side*side*side/6.0/std::sqrt(2.0);
}

Suppose you tried to implement all of these functions at once: you would have made similar mistakes everywhere, and then you would have needed to correct each and every mistake. At this point, you've found your mistakes so you are now ready to implement the rest of the functions.

The benefit here is that if you make one mistake, you can account for it later. Also, you do not get pages and pages of error messages. By explicitly including the formulas in the documentation, you need only refer to it. Additionally, someone who is not a C++ programmer can read and understand your documentation and detect errors in your formulas.

You are welcome to review the example source code in the source directory and finish the remaining functions.

Review

In your own words, why should you start with such a skeleton?

Questions and practice:

1. What benefit is there to calculating each of the values by hand using a calculator instead of just executing your code? What happens if you wrote the wrong function in the calculator?

2. Suppose you are prone to missing closing or opening parentheses or braces. Why would implementing one function at a time benefit you?

3. Many, but not all, of the following implementations have a mistake in them. Some may be compile-time errors, but some may give wrong answers. Can you find them?

double tetrahedron_volume( double side ) {
	return side*side*side/6.0*std::sqrt(2.0);
}

double cube_volume( double side ) {
	return side*side;
}

double octahedron_volume( double side ) {
	return std::sqrt(2.0/3.0*side*side*side;
}

double dodecahedron_volume( double side ) {
	return 1/4*(15.0 + 7.0*std::sqrt(5.0))*side*side*side;
}

double icosahedron_volume( double side ) {
	return 5.0/12.0*3.0 + std::sqrt(5.0)*side*side*side;
}

double tetrahedron_area( double side ) {
	return std::sqrt(3.0)*side*side;
}

double cube_area( double side ) {
	return 6.0*side*side*side;
}

double octahedron_area( double side ) {
	return 2.0*std::sqrt(3.0)*side*side;
}

double dodecahedron_area( double side ) {
	return 3.0*std::sqrt(25.0 + 10.0*std::sqrt(5.0)*side*side;
}

double icosahedron_area( double side ) {
	return 5.0*std::sqrt(3.0)*side*side;
}

4. A keen C++ developer decided to include the formula in the comments:

// @section FORMULA
//               (3.75 + 2.75*sqrt(2))*s*s*s
//////////////////////////////////////////////////////////

double dodecahedron_volume( double side ) {
	return (3.75 + 2.75*sqrt(2))*s*s*s;
}

Do you see any issues with this approach?


Previous lesson Next lesson