C++ – Exception Hierarchy and Parsing Strings with What Message

cexceptionsparsing

I have a piece of code parsing a text file line by line. I have to goals: Testing the syntax of the text and extracting information of it. It is quite likely that syntax errors occur so I want to provice helpful information about where and what.

To give an idea of the problem, I have a text file like the following (simplified example):

1=\tSTRING\tDevice name
15=\tFLOAT\tSpeed
17=\tINTEGER\tMax Speed
18=INTEGER\tMax Speed

As you can guess, the syntax of each line is: <Parameter ID>=\t<Data Type>\t<Description>

My goal is to

  • Return a vector of structs for every parameter.
  • If there is an error, give an error message
    • for example: "Error in Line 2: data type of INTEGER is not allowed"
    • for example: "Error in Line 3: missing tab"

My general structure is:

  • A "function": std::vector<ParameterDAO> ParseText (std::string text)
  • A "sub function" ParameterDAO ParseTextLine (std::string text)
  • As you can guess, ParseTextLine is called by ParseText for each line.
  • Some "subsub functions" used by ParseTextLine (checking spaces in the text, checking elements for validity/range/…

FYI: The strings/substrings itself I parse with regular expressions and some standard string operations (compare, …). But this is not the main point in my question.

OK, now some more details of my implementation:

  1. Any of my functions (ParseText, ParseTextLine, …) can throw an exception.
  2. I always throw the standard exception std::invalid_argument("my error message")
  3. The function "ParseText" always checks for exceptions thrown in one of the sub functions to add the "Error in Line x" message. This is done by getting the what-message of the exception thrown, creating a new string with this message and the line info an rethrow the message:
  4. The code calling "ParseText" also checks for exceptions. If an exceptions has occured it will show the error message (for example "Error in Line 3: missing tab" to the user

Code snippet for 3:

try
{
    Parse_HPA_CATEGORY_SingleLine_THROWS;
}
catch ( std::exception e )
{
    std::string l_ErrorMessage = "Error in Line x: ";
    l_ErrorMessage.append ( e.what () );
    throw std::invalid_argument ( l_ErrorMessage.c_str() );
}

This structure works and has the following benefit:

  • The error message is close to the location where the error occurs (for example close to a string compare or the regular expression).

But there may be some drawbacks / things I am not sure about, too:

  • In the unit test, I have to repeat the string literally ( I don't know if this is actually bad).
  • I read (unfortunately I can't remember where) that the "what message" is usually not used to directly create error messages. Do I misuse the "what message"? Should I maybe deviate an special exception error class from std::exception for every exception case?
  • The ParseText function makes a kind of rethrow. Is there a way to avoid this?

Best Answer

Using exception::what() for text that is show to the end-user is indeed a problem. Suppose your project becomes a real success and you get a request to make a translation of it into some other language. That will be next to impossible if the text you show your users is directly copied from exception::what().

The correct way is indeed to create an exception (hierarchy) for the problems that your parser can encounter. For example:

class FormatError : public std::runtime_error
{
public:
  //...
  void set_line_number(int);
  int get_line_number() const;
private:
  int line_number;
};

class InvalidDataType : public FormatError
{
  // ...
private:
  String dataType;
};
// etc.

Using a common base class makes it easy to add the line number information at a later point (in ParseText) and re-throw the same exception (with throw;)

Related Topic