Assignment 5 - Libraries and Exceptions
due November 5th
This assignment is intended to give you practice in the creation and use of libraries and exception handling techniques.
The assignment consists of three parts.
- You will create a Datetime class consisting of three constructors and an overloaded ostream insertion operator. The Datetime class should be defined in its own header file and the member functions should be defined in a separate source file. After compilation the Datetime class and its member functions will be placed in its own library. The class member functions will be required to throw exceptions as specified below.
- You will write exception classes to handle various error situations described below. These classes will also be placed in their own libary.
- In your main() program, you will read in a data file, partially shown below. The data file consists of Datetime data of three different formats, matching the requirements for each of your three constructors. Some of the data, of course, will be bad. Some exceptions should be thrown and, hopefully, caught and handled. For each line of input data, you will either print the Datetime in the specified format, or handle an exception as specified below. The main() application, when built, will be linking in the Datetime and exceptions libraries.
The input file
Copy this data into your own input file
The input file consists of 50 records in 3 formats:
- "mm/dd/yy hh24:mm:ss" (17 byte char array),
- an empty record, (there is only 1 of these)
- and a long int.
Following are the first six records in the file:
07/08/89 04:21:00 <--- mm/dd/yy hh24:mm:ss
<--- empty record
910113 <--- long int
7256439 <--- long int
10/07/71 06:19:12 <--- mm/dd/yy hh24:mm:ss
2000303765 <--- long int
...
Datetime class requirements
You may implement the Datetime class however you choose, as long as you fulfill the following requirements. You should use the <ctime> header file functions and types to implement your class. Joe used a time_t type (which traditionally has been a typedef for a long) as the data member to store a Datetime value. Your class must include the three constructors specified and a friend or non-class member overloaded insertion operator for the output. The constructors should include exception specifications for any throws that occur in the constructor.
When you have completed and tested this class, it should be separated into a header and source file, compiled, and placed in a library. Note: you will need "access" to your exception types (classes) in the development of this code. You may want to "jointly develop" the exception classes and Datetime class in one file (or one project) until you "get it right".
Note: MS Visual C++ may "warn"on some of the functions as being "unsafe" - DWAI. It may also warn about exception specification ignored - again, DWAI. Finally, if you are working in a 64-bit environment, the <ctime> functions and types may not work as advertised. If you experience problems with this, contact Joe for further instructions.
Here are a few links for <ctime> documentation:
cplusplus.com C++ : Reference : C Library : ctime (time.h)
informIT - C++ Reference Guide: Retrieving the Current Time
- Datetime()
This constructor should create a Datetime object consisting of the current date and time. A call to time(0) might help here. This constructor does not need to throw anything (unless you run out of time - HA! that's a joke!). This constructor will get used for an empty input record.
- Datetime(long number)
This constructor should "assign" it's long argument to a time_t variable. You can then make use of that value, however it best works in your implementation. The constructor should throw an Invalid_time_t type if the argument is not between 1 and 0x7fffffff (that's 2147483647).
- Datetime(string date, string time = string(""))
This constructor has two string arguments, representing a date in the format "mm/dd/yy" (8 characters) and a time in the format "hh24:mm:ss" (also 8 characters). A default second argument should represent time = midnight (hours = 0, minutes = 0, and seconds = 0). The constructor should "parse" the two input strings and use the values to populate a tm struct. Joe used a call to mktime() in his solution. The constructor should throw an Invalid_tm_member if any of the "parsed" values cannot be assigned. For example, if the minutes > 59. It should also throw an Invalid_datetime if the date is invalid, such as February 30th.
Exception classes requirements
The following exception classes should be created and tested. When ready, they should be separated into (a) header(s) file and (a) source file(s), recompiled, and placed into a (only one) library.
- Invalid_time_t: should be derived from the range_error exception class.
- Invalid_tm_member: you decide how to define it, but it should be able to identify which tm member is invalid and the value that was attempted to be assigned.
- Invalid_datetime: derive it from "you decide". This should detect invalid dates, such as February 30th, etc. Note: the mktime() function returns a -1 for these situations.
- CannotFindFile: derive from runtime_error. It should be able to name the file that cannot be found. This will be used to test your input file. Note: in the final execution of your program, you do not need to demonstrate this exception.
Other requirements
- Use the provided input file for your final version.
- Your Datetime class must be divided into a header and source file and be installed in a library.
- Your exception classes must be divided into one or more header files and one or more source files and be installed in a library.
- Your main program will need to include the header files from your Datetime and exception library classes and "link in" the Datetime and exception libraries.
- Your main will need to determine which Datetime constructor to use based on the format of each input record.
- You will need to turn in program listings from all your header and source files and a short description (or console/log listings) of how you performed the compilation and linking of your libraries and main program.
Program output
You output should look "quite similar" to the following. Each exception catch should identify the type of exception and detail regarding the offense.
07/08/1989 04:21:00 AM
10/25/2009 04:47:50 AM
01/11/1970 04:48:33 AM
03/25/1970 04:40:39 PM
10/07/1971 06:19:12 AM
05/21/2033 08:56:05 AM
10/23/1983 01:41:37 PM
Caught Invalid_tm_member: tm_mday invalid value: 33
04/02/1987 01:46:15 PM
01/01/1970 12:52:05 AM
06/01/1974 08:49:01 AM
Caught Invalid_time_t: -999999
Caught Invalid_datetime: bad time in 10/06/18 10:5o:54
01/17/1972 03:08:13 AM
Caught Invalid_datetime: bad date in 06/02/7l 16:20:12 <-- That l is not a number
02/03/2005 02:33:37 AM
...
Additonal Notes
- The Dev-C++ compiler (and probably the gnu compiler) required a destructor in my Invalid_tm_member class. That is probably because I added additional data members to the class. Since the destructor serves no real purpose, I think this is an unnecessary idiosyncrasy of the gnu compiler. None of my other exception classes necessitated this. This is my defintion of the Invalid_tm_member class.
class Invalid_tm_member : public std::logic_error
{
string member;
unsigned short value;
public:
Invalid_tm_member(const string& mem, unsigned short inv_val);
~Invalid_tm_member() throw () {} // Added for Dev-C++
friend std::ostream& operator<<(std::ostream& out, const Invalid_tm_member& err);
};
- You may need to use a clear() on your istringstream if you reuse it, in, say, a while loop. The istringstream object seems to "get funny" after you read from it - it's probably that EOF thing. In my main() I had statements like this:
string buffer;
istringstream sin;
string date , time;
...
while (getline(fin,buffer)) {
sin.clear();
sin.str(buffer);
...
sin >> date >> time;
...
- You'll probably want to do the trys and catches in the "client code" (that's prbably main()). If a function, such as a Datetime constructor throws something, it should really be up to the "client" to decide how (s)he wants to handle the problem (if at all).