spot
This exercize describes in a step-by-step fashion how to write a program reading an image from a file.
You should strive for systematically using all the tools you’ve learned so far:
Do not forget to also systematically test (many times) and after each step the behaviour of your program. Do not hesitate to decompose a step into smaller intermediate ones.
This section should allow you to remember the basics of object oriented programming and of C++ before tackling the DrawQt project. All the steps should be rigorously followed, if, however, some detail is escaping you, do not hesitate to call for help.
Note
Concerning the compilation errors, remember to head toward the page listing the main and usual issues.
This first step -a variant of hello world- installs, tests and validates the projects within your development environment. In our case, the project Image is managed with CMT and subversion.
We’ll start with configuring our svn environment:
$> cd ~/Project
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/Image -m "Added Image project"
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/Image/trunk -m "Added Image project"
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/Image/branches -m "Added Image project"
$> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/Image/tags -m "Added Image project"
-m "Added Image project"
We’ll need the Interfaces package to access and use the system libraries. Fetch it from the svn repository called Enseignement:
$> cd ~/Project
$> svn export https://svn.lal.in2p3.fr/projects/Enseignement/LAL-Info/tags/head/Interfaces \
Interfaces
Now, create your new CMT package:
$> cmt create Image v1
$> svn co https://svn.lal.in2p3.fr/projects/Etudiants/ens<n>/Image/trunk Image
$> cd Image
Edit the requirements file like so:
1 2 3 4 5 6 7 8 9 10 | package Image
# for basics system libraries
use Platform v1r* ${HOME}/Project/Interfaces
# to build an application readImage.exe
application readImage readImage.cpp
# define an action 'read' to execute readImage.exe
action read $(bin)/readImage.exe
|
Then, edit the file src/readImage.cpp along these lines:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /**
* @file readImage.cpp
* @author LAL ens <ens@lal.in2p3.fr>
* @date February 2006
*
* @brief Read an image from a file image.txt
*
*
*/
#include <iostream>
int main (int argc, char **argv)
{
// print an informative message on the screen
// ...
return (0);
}
|
Now we are left with building and running/testing the application:
$> cmt make
$> cmt read
At this point, you can commit and save your first version of the program in svn, after having removed the not-important files (history-wise, i.e. the files you do not wish to track in svn):
$> rm -r ../Darwin
$> rm ../cmt/Makefile ../cmt/*.csh ../cmt/*.sh
$> cd ..
Note
You can also instruct svn to ignore certain files or directories by editing the svn configuration file ~/.subversion/config along the section [miscellany] :
Finally, add the current tree to svn, save and synchronize your workarea and the repository:
$> svn add cmt src
$> svn commit -m "Added Image package"
$> svn update
You can check the repository is up-to-date using the by now well known svn status command in the correct directory.
Create a directory to hold the input data:
$> mkdir ./data
Retrieve the Image file with Ctrl-click (and then Save the link or Enregistrer la cible or Telecharger le fichier...). Save the file under the ../data directory.
The structure and format of this file is described over there.
Commit the data file in svn:
$> svn add data
$> svn commit -m "adding data directory" ./data
Here are the snippets of code which deal with opening the data file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ...
#include <fstream>
...
const std::string filename = "../data/image.txt";
std::ifstream f;
f.open(filename.c_str());
if (!f.is_open()) {
std::cout << "error. file [" << filename << "] could not be found."
<< std::endl;
f.close();
return (0);
}
std::cout << "file: [" << filename << "] open." << std::endl;
f.close();
...
return (0);
|
More detailed informations about input/output functions can be found there.
Now, we can build and test our application like so:
$> cmt make
$> cmt read
Eventually, we commit and save into svn.
Note
remember we work in the src directory...
The following snippet of code shows how to loop over the content of a file, simply reading it word by word:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <iostream>
#include <fstream>
#include <string>
int main (int argc, char **argv)
{
std::cout << "Application readImage." << std::endl;
const std::string filename = "../data/image.txt";
std::ifstream f;
f.open( filename.c_str() );
if (!f.is_open()) {
std::cout << "error. file [" << filename << "] could not be found."
<< std::endl;
f.close();
return (0);
}
std::string word;
std::cout << "file: [" << filename << "] open." << std::endl;
// put the word-by-word reading loop here and
// print out the word which has been read
while (!f.eof()) {
//...
}
return (0);
}
|
Warning
The reading function will only return a word back to you as long as the end of the file is not reached. Ergo, you shouldn’t forget to test for that edge case.
Build this application, test it and commit your changes in svn once everything is behaving correctly.
Before going further in the implementation of our reading application, we’ll create a C++ class Image. This class will allow us to properly isolate the data members from the operations associated with or applied on our images. This is one of the pillars of object-oriented programming.
A presentation on C++ classes is available here.
In order to cleanly separate the structure of the class itself from its implementation, we’ll create 2 files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /**
* @file image.h
* @brief File holding the description of the class Image
* @author LAL ens <ens@lal.in2p3.fr>
* @date February 2009
*/
#ifndef PROJECT_IMAGE_H
#define PROJECT_IMAGE_H 1
/**
* @brief
*/
class <Enter the name of your class>
{
public:
/** @brief ...
*/
Image();
/** ...
*/
~Image();
};
#endif // !PROJECT_IMAGE_H
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /**
* @file image.cpp
* @brief ...
* @date February 2009
*/
#include "image.h"
Image::Image()
{
}
Image::~Image()
{
}
|
You need to modify the cmt/requirements file for the build system to be aware of these new files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package Image
# add the 'include' directory to the list of directories
# the compiler should know about
include_dirs $(IMAGEROOT)/include
# for basics system libraries
use Platform v1r* ${HOME}/Project/Interfaces
# to build an application readImage.exe
application readImage readImage.cpp image.cpp
# define an action 'read' to execute readImage.exe
action read $(bin)/readImage.exe
|
Rebuild everything, test and commit in svn.
In this step, we’ll isolate the reading proper of a file into a method of the class Image. (Don’t forget to add all #include if necessary) This method will later on be integrated and leveraged during the hands-on session on DrawQt.
You shall add the method ReadFile() with the following signature:
- bool ReadFile(const std::string& filename)
to your new class Image; the method ReadFile() will return a boolean in order to indicate whether the file scan ran into an error or not.
When this work is completed, the main() function of your program readImage should only contain:
Note that the error handling is not mandatory yet (this will be for a later step.)
Build, test and commit in svn.
Using the informations about the image file format documentation, we’ll re-implement the reading of the file. Previously, in step 3 we were reading the input file ‘word by word’ (or ‘token by token’.)
But now that the format of the image file and its grammar is known, we build upon it to greatly simplify the reading of such files.
read a word if not an expected word: return an error read next word
Note
To read next token into a word you have already seen the f >> my_word method; if you want to read the next token into an int, you could also use the same method f >> my_int
This exercize will ask you to go back to your Image package and comment it as best as possible with the tool Doxygen.
We will now improve our application with some documentation generated with doxygen. In order to ease the process of documenting the code, we’ll first add a few rules to CMT so it can steer doxygen.
Open the cmt/requirements file and add the following lines at the end:
1 2 | # define an action to generate the documentation
document doxygen doc -group=documentation TO=../doc
|
Now, we have to tell CMT that it has to read is requirement file :
$> cmt config
We’ll also have to create a doc directory to store this documentation. Create it as: image/doc.
Now, we have to configure doxygen thanks to a dedicated file named Doxyfile. This file is available here, save it under this new doc directory.
We can build and browse the documentation like so:
$> cmt make doc
$> open ../doc/html/index.html
We can now modify the documentation of our code and inspect these modifications once the documentation has been regenerated. Once the result looks satisfying, save and commit the modifications.
Note
It isn’t necessary to add ALL the documentation files to svn. Indeed, these output files can be automatically generated from the Doxyfile. It is thus sufficient to just add that file to the repository.
$> svn add ../doc
$> svn revert --depth infinity ../doc/html
$> svn commit -m "added the doc generation with Doxygen management"
You could also update your ~/.subversion/config file in order to definitely ignore the html directory.