This is the second part of my article about auto generating a C++ class using Perl. Download the full script.
On Entry
As I mentioned in the previous article the first thing that happens on running the script is calling parse_input()
. This is not strictly true as before that I need to set up and initialise the variables that will be set by it. This is done in the following code.
$class_name = ""; $source = 0; $header = 0; $query = 0; $source_file_name = ""; $header_file_name = ""; parse_input(); if ($class_name eq "") { print("No name given\n"); exit 1; } $header_file_name = $class_name . ".h"; $source_file_name = $class_name . ".C";
So I have flags for which functions to run and a class name to fill in before I parse the input. Then when these have been filled in there is some error checking. This is because the whole point of this tool is to generate files and so without file names there is nothing sensible it can do. It then makes the header and source file names by combining the class name with a .h or .C extension respectively, using the concatenation operator which is .
in Perl. Then various functions are called depending on the flags set, I feel this is essentially the main()
loop of the script.
if ($query) { query_for_methods(); } if ($header) { write_top_of_header(); write_header_methods(); write_bottom_of_header(); } if ($source) { write_top_of_source(); write_source_methods(); }
Getting Methods
This is the function query_for_methods()
#------------------------------------------------------------------------------ sub query_for_methods() { print "Enter q at anytime to quit.\n"; while(1) { print "Input method name\n"; $name = <>; chomp($name); if ($name eq "q") { last; } print "Input method return value\n"; $ret_val = <>; chomp($ret_val); if ($ret_val eq "q") { last; } print "Input all method arguments eg. const int& i, bool t \n"; $arguments = <>; chomp($arguments); if ($arguments eq "q") { last; } push(@method_types, $ret_val); push(@method_names, $name); push(@method_arguments, $arguments); } }
This is not exactly the most elegant method of getting information from the command line. It tells you the escape character ‘q’ which I think should not be needed for either a method name, type or argument. Then it asks for the three parts which make up a C++ function the name, return value and arguments. This function removes newlines (chomp
) from them and pushes each into an array.
Writing the Files
As you can see from one of the above code snippets there are five functions to write the header/source files; write_top_of_header(), write_header_methods(), write_bottom_of_header(), write_top_of_source(),
and write_source_methods()
. I will just be looking at part of write_top_of_header()
and write_header_methods()
as the other functions work in the same way.
This is the upper part of the function to write the top part of the header file. It is not the whole function as most of that is a string to be written to a file. In this function the file is opened using the “>” mode which means that it is write only and will overwrite any existing files.
#------------------------------------------------------------------------------ sub write_top_of_header() { $if_def_name = "_" . "$class_name" . "_H"; open (MYFILE, ">", "$header_file_name"); print (MYFILE "//============================================================================= // $class_name.h
After the file is opened it should be closed, this is done here at the end of the function.
close (MYFILE); }
For the write_header_methods()
code the open method is different, it is “<<" which means to append to a file and so will not overwrite any existing file. Then the arrays of method names, return types and values are iterated over printing out a method for each one. Note that this means that all methods are public by default in any file generated by this script.
#------------------------------------------------------------------------------ sub write_header_methods() { for ($i = 0; $i < scalar(@method_names) ; $i++) { open (MYFILE, ">>", "$header_file_name"); print (MYFILE " $method_types[$i] $method_names[$i]($method_arguments[$i]); // <Description> // Precondition: "); } close (MYFILE); }
Improvements
Having shown this to Dom, one of my friends at work, he told me of some improvements I could make.
In parse_input()
I set $parse = $argv[0]
and then call shift()
on the array of arguments each time. Instead of this I could have just used $parse = shift($argv[0]);
as shift()
returns the value which was popped off of the array. In my copy of this file I have made this change, but I taken his next piece of advise. He also said that rather than parse the inputs myself using a procedural loop I should call a function like Getopt::long
or many of the others from the Getopt module. My reasons for not doing this are perhaps not the greatest justification, I feel that I want to have my code look similar across multiple scripts and languages. This means that I would rather have a loop or switch statement than a more normal for Perl function which would look something like this:
GetOptions ('header' => \$header, 'source' => \$source);
There is an improvement that I spotted while writing this article. When I write the methods in either the header or source files I have this:
for ($i = 0; $i < scalar(@method_names) ; $i++) { open (MYFILE, ">>", "$source_file_name");
The call to open()
should be before the for loop. This because it only needs to be opened once, and also it is unnecessary overhead. I have therefore made these changes to the script, and I hope this has been useful to you.
Download this script.
Note: I will follow this up with a post discussing my coding style. This will also help explain why this script generates a .C source file from C convention rather than a .cpp file which is standard.
1 Response to “Auto-generate C++ Classes Using Perl, Part II”