s t r i n g␣s1␣= ss . str ();
c o u t␣< <␣s1␣< < " \ n " ;
Once you have built up the string you want, call the member function .str() to convert the stringstream to a string.
We won’t perform much string manipulation in this book and the perfor-mance won’t be a big issue for us. But using a stringstream is also great for testing. If you have a function that writes to an ostream you can test that it works by seeing if it writes to stringstream correctly. You can then be confident it will work when you use an ofstream to write to a file. You can even be confident it will work when you write to some more exotic kind of stream that represents an Internet connection.
This is another example of polymorphism and shows how polymorphism makes testing easier.
7.6 Writing a pie chart
It may surprise you to learn that we now know enough C++ to be able to produce some charts. Admittedly there is a trick: We will actually produce the data for a chart in C++ and then use an external program to produce the chart itself.
Given this idea, the simplest way to chart in C++ is to simply write the data to a file and then create the chart using a spreadsheet program such as Excel or OpenOffice Calc.
Here is the code needed to write a chart in a format that can be easily understood by a spreadsheet program.
v o i d␣w r i t e C S V C h a r t D a t a (␣o s t r e a m &␣out , c o n s t␣vector < double >&␣x , c o n s t␣vector < double >&␣y␣)␣{ A S S E R T (␣x . s i z e ( ) = = y . s i z e ( ) ) ; int␣n␣=␣x . s i z e ();
for␣( int␣i =0;␣i < n ;␣i ++)␣{
out␣< <␣x [ i ]␣< < " , " < < y [ i ]␣< < " \ n " ; }
}
v o i d␣w r i t e C S V C h a r t (␣c o n s t␣s t r i n g &␣f i l e n a m e , c o n s t␣vector < double >&␣x ,
c o n s t␣vector < double >&␣y␣)␣{ o f s t r e a m␣out ;
out . o p e n (␣f i l e n a m e . c _ s t r ()␣);
w r i t e C S V C h a r t D a t a (␣out ,␣x ,␣y␣);
out . c l o s e ();
}
To make this part of a library we need to declare it in the header:
v o i d␣w r i t e C S V C h a r t (␣c o n s t␣std :: s t r i n g &␣f i l e n a m e , c o n s t␣std :: vector < double >&␣x ,
c o n s t␣std :: vector < double >&␣y␣);
The function declaration in the header, uses the qualification std:: a lot.
We’ve had to fully qualify every type name. The reason is that you should never write using␣namespace␣std; in a header file so unfortunately all these boring std:: prefixes are required.
Together these two functions give a very sensible solution to the problem of writing a chart in C++. It doesn’t feel very satisfying, however, because one still has to manually turn the data into a chart using the spreadsheet program. We can do a little better.
7.6.1 A web-based chart
It is surprisingly easy to create a web page containing a pie chart. Here are the steps:
• Create a file called myPieChart.html. Open it with a text editor.
• Copy the code below into your file1. You can also download essentially equivalent code from the website:
https://google-developers.appspot.com/chart/.
• Save the file.
• Open the file in a web browser.
< h t m l >
< h e a d >
< ! - - L o a d␣t h e␣A J A X␣A P I - - >
1 This code is taken from Google Charts’ online documentation and altered slightly to fit the page. The code is distributed under the Apache 2.0 license. http://www.apache.org/
licenses/LICENSE-2.0
< s c r i p t␣t y p e= " t e x t / j a v a s c r i p t "
If you have done everything correctly, when you double click on the file it should show a pie chart like the one in Figure 7.1.
You may wonder how this works. In a nutshell it uses Google’s web-based charting library, which makes it easy to create charts inside web documents (.html files). We haven’t got time to learn HTML and Javascript, the two languages involved in creating this chart. However, this isn’t a significant problem. We can simply use our intelligence to work out how to change the example code to print the pie chart of our choice.
This example code comes straight from Google’s own documentation for Google Charts. If you look through their online documentation it will give you plenty of ideas about how you can change the chart to meet your needs.
We’ve been learning to program C++ from the bottom up. In other words we’ve been learning all the technical details step by step. Another route to
Pizza Toppings
Mushrooms Salami Spinach
44.4%
33.3%
22.2%
FIGURE 7.1: An example pie chart
learning a language is by immersion: you look at examples and change them.
You may never learn all the strict rules of the language, but you can get a lot done with limited knowledge. So right now we’re learning HTML and Javascript by immersion.
As an exercise, change the pie chart example so it produces the pie chart of your choice. Just in case it isn’t obvious, the only bit of the code you need to pay attention to are the lines:
d a t a . a d d R o w s ([
[ ’ M u s h r o o m s ’ ,␣4] , [ ’ Salami ’ ,␣2] , [ ’ S pi na c h ’ ,␣3]
]);
The end result is that writing a pie chart won’t be much harder than generating a file containing the data. There’s just a bit of extra boilerplate at the top and bottom of the file that we don’t need to understand.
Let us run through the entire process of creating a function to generate pie charts.
7.6.2 Create a header file
• Create a new header file.
• Call the file charts.h
• All header files should start with #pragma␣once
• Include standard libraries with #include␣"stdafx.h"
• (We’ll cover tests later)
# p r a g m a␣o n c e
# i n c l u d e␣" s t d a f x . h "
7.6.3 Write a source file
• Create a new source file
• Call the file charts.cpp
• All source files should #include the related header file
• (We’ll cover tests later)
# i n c l u d e␣" c h a r t s . h "
7.6.4 Enable testing in your files In charts.h
v o i d␣t e s t C h a r t s ();
In main.cpp int␣m a i n ()␣{
t e s t M a t l i b ();
t e s t G e o m e t r y ();
t e s t C h a r t s ();
t e s t U s a g e E x a m p l e s ();
}
In charts.cpp
void␣testCharts()␣{
}
7.6.5 Write functions to generate the boiler plate
• We pass an ostream& reference to the function
• We use \" to escape quotes in quotes.
• The spacing in HTML files isn’t very important, so this function doesn’t reproduce the spacing of Google’s example pie chart precisely. This was done so we could fit the code on the page.
s t a t i c␣v o i d␣w r i t e T o p B o i l e r P l a t e O f P i e C h a r t (␣o s t r e a m &␣out␣)␣{
Writing a function for the bottom boiler plate code is just as easy.
s t a t i c␣v o i d␣w r i t e B o t t o m B o i l e r P l a t e O f P i e C h a r t (
7.6.6 Write a simple version of the chart data
The hardest bit of code will be writing out the pie chart data. For the time being we “cheat” and just write a simplified function that will print out some fixed text that we hope will work.
This isn’t really cheating, just a sensible practice. We work in small pieces.
Once you’ve solved one simple problem, move on to the next simple problem.
s t a t i c␣v o i d␣w r i t e F i x e d P i e C h a r t D a t a (␣o s t r e a m &␣out )␣{
out < < " d a t a . a d d R o w s ([\ n " ; out < < " [ ’ B a n a n a s ’ ,␣100] ,\ n " ; out < < " [ ’ A p p l e s ’ ,␣200] ,\ n " ; out < < " [ ’ K u m q u a t s ’ ,␣1 5 0 ] \ n " ; out < < " ] ) ; \ n " ;
}
7.6.7 Write a test of what we’ve done so far s t a t i c␣v o i d␣t e s t F i x e d P i e C h a r t ()␣{
o f s t r e a m␣out ;
out . o p e n ( " F i x e d P i e C h a r t . h t m l " );
w r i t e T o p B o i l e r P l a t e O f P i e C h a r t ( out );
w r i t e F i x e d P i e C h a r t D a t a (␣out␣);
w r i t e B o t t o m B o i l e r P l a t e O f P i e C h a r t (␣out␣);
out . c l o s e ();
}
void␣testCharts()␣{
␣␣␣␣TEST(␣testFixedPieChart␣);
}
We’ve written enough code to test, so let’s run it. When you run this test using the standard development environment set-up, it will create the file in the same folder as main.cpp.
7.6.8 Write the interesting code
Let us be clear on what the interesting bit of code must do. Given a string of labels it should produce output that looks like this:
data.addRows([
[’Bananas’, 100], [’Apples’, 200], [’Kumquats’, 150]
]);
The last line is special—there is no comma. Other than that it all looks straightforward.
s t a t i c␣v o i d␣w r i t e D a t a O f P i e C h a r t (␣o s t r e a m &␣out ,
c o n s t␣vector < string >&␣labels , c o n s t␣vector < double >&␣v a l u e s )␣{
out < <␣" d a t a . a d d R o w s ([\ n " ; int␣n L a b e l s␣=␣l a b e l s . s i z e ();
for␣( int␣i =0;␣i < n L a b e l s ;␣i ++)␣{ s t r i n g␣l a b e l␣=␣l a b e l s [ i ];
d o u b l e␣v a l u e␣=␣v a l u e s [ i ];
out < < " [ ’ " < < label < < " ’ ,␣" < < value < < " ] " ; if␣( i != nLabels -1)␣{
out < < " , " ; }
out < < " \ n " ; }
out < < " ] ) ; \ n " ; }
There is one subtle problem. For simplicity we have assumed that the labels don’t contain quotation marks or other special characters. We leave it as an exercise to fix this issue.
Danger!
It is important to fix issues with special characters in production software.
Hackers love bugs based around quotation marks. For example, when using a web application, they might try choosing the peculiar user name
Dave’); DELETE FROM Users;
--Here the hacker is guessing that you are using an SQL database and they are hoping to trick you into executing the following SQL code on your database2: INSERT␣INTO␣Users␣VALUES␣(’Dave’);␣DELETE␣FROM␣Users;␣--’) This code would first create a new User called “Dave”, but then would delete the entire database table Users! The issue is that the ’ character has a special meaning in SQL just as it does in C++ and so it needs to be escaped when it is used as part of a string.
7.6.9 Testing the interesting code
To test the function writeDataOfPieChart we write a test that finds the text output by the function writeDataOfPieChart and then compares it to the expected value. To do this we first set up some dummy data for our pie chart. We then call writeDataOfPieChart and store the output using a stringstream.
s t a t i c␣v o i d␣t e s t P i e C h a r t D a t a ()␣{
//␣t h i s␣t e s t␣a u t o m a t e s␣the␣c h e c k i n g s t r i n g s t r e a m␣out ;
vector < string >␣l a b e l s ( 3 ) ; vector < double >␣v a l s ( 3 ) ; for␣( int␣i =0;␣i <3;␣i ++)␣{
s t r i n g s t r e a m␣ss ; ss < < " A␣L a b e l␣" < < i ; l a b e l s [ i ]␣= ss . str ();
I N F O (␣l a b e l s [ i ]␣);
v a l s [ i ]=( d o u b l e ) i ; }
w r i t e D a t a O f P i e C h a r t (␣out , labels ,
v a l s␣);
s t r i n g␣a s S t r i n g␣=␣out . str ();
We can now compare the output of writeDataOfPieChart to the output we are expecting.
s t r i n g s t r e a m␣e x p e c t e d ;
e x p e c t e d < < " d a t a . a d d R o w s ([\ n " ; e x p e c t e d < < " [ ’ A␣L a b e l␣0 ’ ,␣0] ,\ n " ; e x p e c t e d < < " [ ’ A␣L a b e l␣1 ’ ,␣1] ,\ n " ; e x p e c t e d < < " [ ’ A␣L a b e l␣2 ’ ,␣2]\ n " ; e x p e c t e d < < " ] ) ; \ n " ;
s t r i n g␣e x p e c t e d S t r␣=␣e x p e c t e d . str ();
A S S E R T (␣a s S t r i n g == e x p e c t e d S t r␣);
}
This gives a test that writeDataOfPieChart behaves in the way that we want.
• Since fstream and stringstream are both types of ostream, the inter-esting code was easy to test.
• You might have thought it would be impossible to write a useful test for code that generates a chart. Many people would argue that you need a human to look at the chart to test the code. However, we have just shown that it is perfectly possible to write meaningful tests of almost any code. If you can’t test it, you’ve designed your code incorrectly or don’t understand the problem properly.
7.6.10 Wrap it all up into a single function
The function definition below makes it easy to create a pie chart with given labels and values from C++. The pie chart will be saved in the given file.
v o i d␣p i e C h a r t (␣c o n s t␣s t r i n g &␣file ,
c o n s t␣vector < string >&␣labels , c o n s t␣vector < double >&␣v a l u e s␣)␣{ o f s t r e a m␣out ;
out . o p e n ( f i l e . c _ s t r ( ) ) ;
w r i t e T o p B o i l e r P l a t e O f P i e C h a r t ( out );
w r i t e D a t a O f P i e C h a r t (␣out ,␣labels ,␣v a l u e s␣);
w r i t e B o t t o m B o i l e r P l a t e O f P i e C h a r t (␣out␣);
out . c l o s e ();
}
Hopefully you will agree that this function is very simple. We have used functions to divide our code into manageable pieces which together produce an interesting result.
If we want to make our function available in other files, we will need to put a declaration into the header files as follows:
v o i d␣p i e C h a r t (␣std :: s t r i n g &␣file ,
std :: vector < std :: string >&␣labels , std :: vector < double >&␣v a l u e s␣);
This is copied from the code in the .cpp file except we’ve had to put in lots of std:: statements since you should never write using␣namespace␣std; in a header file.