C ++ Interpreter: how to emit error messages?

I want to generate dynamic error messages like all interpreters like:

Name error: Undefined variable

      

will be persistent, however I want to achieve:

Name error: Undefined variable 'X', in line 1

      

Okay. The line number is really not a problem: every error message must have a line number, so I added that to the error emitter function:

Error( ErrType type, string msg, int line );

      

So where is my problem?

How do I get 'X'

in Undefined variable *

?

  • I can't use sprintf as it doesn't support strings, but I use them everywhere
  • I can't just use cout and hook everything up as I want the error messages to be jerky.
  • I want everything to be in one function like above, Error()

How do I collect dynamic error messages?

For instance: Error( Name, sprintf("Undefined variable %s", myVariableName ), lineNum );

(But it myVariableName

is a string, and sprintf

will be corrupted)

+2


a source to share


2 answers


You can get a null terminated C string from myVariableName

by calling c_str

:

myVariableName.c_str()

      



Please note that the usage sprintf

is incorrect; the first parameter sprintf

takes a buffer to hold the formatted string. If you are using std::string

in your program, why use at all sprintf

? If it Error

does std::string

, you can simply use string concatenation:

Error(Name, "Undefined variable " + myVariableName, lineNum);

      

+3


a source


Clang decides that if their diagnostics are overloaded operator<<

, streaming the required arguments that I accepted in my compiler too

DiagnosticBuilder Error( ErrType type, string msg, int line );

      

Then you can call it like

Error(Serious, "Variable % is not known", lineNumber) << var;

      



When the builder diagnostic destructor is called, an error is thrown.

struct DiagnosticBuilder {
  DiagnosticBuilder(std::string const& format)
    :m_emit(true), m_format(format) 
  { }
  DiagnosticBuilder(DiagnosticBuilder const& other) 
    :m_emit(true), m_format(other.m_format), m_args(other.m_args) {
    other.m_emit = false;
  }
  ~DiagnosticBuilder() {
    if(m_emit) {
      /* iterate over m_format, and print the next arg 
         everytime you hit '%' */
    }
  }

  DiagnosticBuilder &operator<<(string const& s) {
    m_args.push_back(s);
    return *this;
  }
  DiagnosticBuilder &operator<<(int n) {
    std::ostringstream oss; oss << n;
    m_args.push_back(oss.str());
    return *this;
  }
  // ...
private:
  mutable bool m_emit;
  std::string m_format;
  std::vector<std::string> m_args;
};

      

I find this way of doing it quite conveniently. You can extend it to support multiple languages ​​by numbering the argument placeholders, eg "Variable %2 isn't found in scope %1"

.

+3


a source







All Articles