Data vs control (struct and enum types)¶
This section will discuss data driven programming. Before that, you will learn two C++ data types you need in order to understand the explanation.
Record type (struct in C++)¶
Many programming languages allow the programmer to define their own data types which can be used to group different types of data elements. In a way, it is like a primitive version of class types: There is no function interface, but all the fields can be accessed directly. A data type like this is often called a record or, in C++, struct.
In C++, a record is defined using the reserved word struct
:
struct Product {
string product_name;
double price;
};
After the definition above, the new type Product
can be used pretty much
the same way as the other data types:
Product item = {"soap", 1.23};
You can access the partial information of a Product
type variable
with the .
operator:
item.price = 0.9 * item.price; // Discount 10 %
cout << item.product_name << ": " << item.price << endl;
item = { "orange", 0.45 };
You can use the struct as a value that you store in the STL container, as a parameter of a function (a value or a reference parameter), and as a return value.
Enumeration type¶
Sometimes you need a variable that can only have certain values.
For example, let us say that the status of a book in the library software
can only be either on the shelf
, borrowed
, reserved
, or lost
.
Naturally, it is possible to use the types string
or int
, but they
would allow many other values besides the aforementioned ones.
That is why they are not the best options for storing the status of a book.
There is a data type called enumeration type, for which all the possible values (elements) are defined by the programmer.
In C++, the enumeration type is defined by the reserved word enum
:
enum Type_name { element_1, ... , element_n };
Above, we defined a new type called Type_name
, and it contains the
elements element_1
, …, and element_n
.
In a more concrete way:
enum Book_status { ON_THE_SELF, BORROWED, RESERVED, LOST };
The elements of the enumeration type are integer constants, and the C++
compiler
defines their values automatically: element_1 = 0
, element_2 = 1
, etc.
If the programmer wishes, they can define the values of the elements:
enum Type_name { element_1 = value_1, ... , element_n = value_n };
Sometimes it is practical that the enumeration type is able to express its own size, in other words, the amount of its values:
enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY,
AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, MONTHS_IN_YEAR
};
Data driven programming¶
A computer program consists of data and the various commands targeting it. In reality, the matter is not divided that clearly, because in some situations, data can replace some of the commands and vice versa. The so-called data driven or data directed designing and programming utilize that characteristic.
The idea of data driven programming is to design and implement data
structures within a program that reduce the amount of data processing commands,
that is, to replace certain commands by data.
You can see this more clearly from the example using the beforementioned
types struct
and enum
.
(The complete program code of the example is located in Git:
examples/04/datadrivenprogramming).
Let us first think about a snippet of code in which we will not use the philosophy of data driven programming:
#include <string>
enum PostalAbbreviation {AL, AK, AZ, AR, CA, CO, ERROR_CODE}; // Excluded the rest 44 elements
// Version 1: First idea
PostalAbbreviation name_to_abbreviation(std::string name)
{
if(name == "Alabama"){
return AL;
} else if (name == "Alaska"){
return AK;
} else if (name == "Arizona"){
return AZ;
} else if (name == "Arkansas"){
return AR;
} else if (name == "California"){
return CA;
} else if (name == "Colorado"){
return CO;
} else { // Excluded 44 "else if" blocks
return ERROR_CODE;
}
}
The if
structure above would be a lot longer in reality if the rest of
the code with the remaining 44 states was written out.
If the same piece of code will be written according to the principles of data
driven programming, we could get rid of the long if
sentence like this:
#include <string>
#include <vector>
enum PostalAbbreviation {AL, AK, AZ, AR, CA, CO, ERROR_CODE}; // Excluded the rest 44 elements
struct StateInfo {
std::string name;
PostalAbbreviation abbreviation;
};
const std::vector<StateInfo> STATES = {
{ "Alabama", AL },
{ "Alaska", AK },
{ "Arizona", AZ },
{ "Arkansas", AR },
{ "California", CA },
{ "Colorado", CO } // Excluded 44 lines
};
// Version 2: Better solution
PostalAbbreviation name_to_abbreviation(std::string name)
{
for(auto s : STATES) {
if(name == s.name){
return s.abbreviation;
}
}
return ERROR_CODE;
}
The difference between these two examples is clear:
After going through a
little bit of trouble to design and initialize a suitable data structure,
the actual function name_to_abbreviation
was decreased significantly.
The technique of data driven programming is always - in one way or
another – the use of
pre-processed information comprised of initial values and the
results from them.
The benefits of data driven programming include:
- The programs created are usually shorter, but still clear.
- The possibility of making errors is smaller.
- The programs are easier to expand and maintain.
A clear sign of a need for data driven programming within a program
is an if
structure that grows out of proportion (or a switch
structure, which we have not studied so far.)