Handling error/status messages by interface to C++ programs

  • Thread starter Leslaw Bieniasz
  • Start date
L

Leslaw Bieniasz

Cracow, 8.09.2009

Hi,

I am writing a C++ program that does a job similar to a typical compiler:
It reads some source text, analyses it for errors, and performs some
conversion to a target format. I have a problem how to design the
communication between my code and user interfaces, to achieve
the following goals:

1) The code should be able to run either in the console mode, or
within a Windows GUI, but the kernel part of the code should be designed
in such a way so that it is independent on the interface.

2) The code should make available to the program user a list of
errors detected during its operation, with some additional information
about the location of the errors in the source text analysed.
Also, the user should be informed about the current status of
the program operation, by suitable messages.

3) The operation of the code is rather fast, so that it seems that
it is not necessary to run it in a separate thread (but who knows
what will be needed in the future?)

Owing to these features, I notice an analogy of my code to some of the
commercial C++ compilers, such as, for example, Borland compilers,
like C++ Builder. They can also run either in the console or Windows mode,
and send appropriate error and status messages. In the case of the Windows
GUI, the Builder displays status messages in a small Dialog-type window,
opened for the time of the compilation, and error messages are
added to a ListBox-type window. Later on, a user can click on any
of the error messages, which causes that a part of the
source text, containing the particular error, is displayed in another window.
In the case of the console mode, the messages are sent to the console.

I would like to have a similar behaviour in my code, so that if anybody
knows how such things are designed, please help me.

In the case of console programs, the easiest thing to do is to send
various messages to an output stream, during the program operation, for example:

cout << "Error detected: missing right parenthesis".

However, this solution does not seem the best if the same code is to run
also under graphical GUI, because there is no direct way to connect the output
stream to the various windows. It therefore seems that the interface-independent
kernel of my code should not send the messages but only keep a list of them somewhere.
The interface would later display the messages in this or other way. But this solution
is also not perfect, because then there would be a time shift between the actual
events and the information about the events, revealed to the user.

A related issue is how the code should behave internally when it detects
an error. Is this a good idea to use exceptions in such cases? If yes,
which part of the code (or interface?) should be responsible for
catching the exceptions?

I have to say I have already asked many programmers over the years,
about the above problems, and I have never obtained any real help.
This seems strange, as I can imagine these problems frequently arise
in the design of any serious large applications.

I would appreciate very much your help, ideally together with
some examples, using OOP and C++.

Leslaw
 
F

Francesco

                                        Cracow, 8.09.2009

Hi,

I am writing a C++ program that does a job similar to a typical compiler:
It reads some source text, analyses it for errors, and performs some
conversion to a target format. I have a problem how to design the
communication between my code and user interfaces, to achieve
the following goals:

1) The code should be able to run either in the console mode, or
within a Windows GUI, but the kernel part of the code should be designed
in such a way so that it is independent on the interface.

2) The code should make available to the program user a list of
errors detected during its operation, with some additional information
about the location of the errors in the source text analysed.
Also, the user should be informed about the current status of
the program operation, by suitable messages.

Hi Leslaw,
you can use the same technique used by GCC, that is, the compiler
itself is a console program that is called by passing the source
filename and the options to it, then it performs a single run dropping
all messages to the standard output stream.

When you have to call it from a GUI interface, simply intercept its
output and display it on your interface.

Actually, that compiler is not interactive, and I believe it is the
best technique - after all, once it intercepts a problem in the source
you have either to change the source or change its parameters, and
then the best solution is to restart it from scratch with the new
input.
3) The operation of the code is rather fast, so that it seems that
it is not necessary to run it in a separate thread (but who knows
what will be needed in the future?)

Owing to these features, I notice an analogy of my code to some of the
commercial C++ compilers, such as, for example, Borland compilers,
like C++ Builder. They can also run either in the console or Windows mode,
and send appropriate error and status messages. In the case of the Windows
GUI, the Builder displays status messages in a small Dialog-type window,
opened for the time of the compilation, and error messages are
added to a ListBox-type window. Later on, a user can click on any
of the error messages, which causes that a part of the
source text, containing the particular error, is displayed in another window.

Your GUI can simply display the messages in a clickable list and
"decode" each error message - which should report the filename and the
line of code where the error happened.

Hope this small help comes useful somehow.

Have good coding, your project is really interesting and would be nice
to read here about your progresses.

Best regards,
Francesco
 
F

Francesco

                                        Cracow, 8.09.2009
In the case of console programs, the easiest thing to do is to send
various messages to an output stream, during the program operation, for example:

cout << "Error detected: missing right parenthesis".

However, this solution does not seem the best if the same code is to run
also under graphical GUI, because there is no direct way to connect the output
stream to the various windows.

I think I have overlooked this point. Are you sure that you cannot
intercept the output? Any operating system should be able to redirect
the output.

For example, under Windows the ">" character, appended to a program
call, redirects the output to anything on the right side of that
character. For example...

c:\compiler.exe sourcefilename -option > messenger.exe

....redirects the output of compiler.exe to an hypothetical small
messenger.exe utility whose unique task is to transform the output
into a list which can be polled at runtime via the Windows API.

Another solution, really simple, would be to dump the output to a file
and then read it from your interface:
c:\compiler.exe sourcefilename -option > dump.txt

Hope this helps, once more.
Just a couple of pointers that you will be surely able to dig.

Kind regards,
Francesco
 
V

Victor Bazarov

Francesco said:
I think I have overlooked this point. Are you sure that you cannot
intercept the output? Any operating system should be able to redirect
the output.

For example, under Windows the ">" character, appended to a program
call, redirects the output to anything on the right side of that
character. For example...

c:\compiler.exe sourcefilename -option > messenger.exe

I believe you've confused two separate elements of the MS-DOS interface.
The '>' redirects the output of the left operand (executable) to the
file specified as the right operand, while '|' ("pipe") redirects the
output and makes it standard *input* of the executable on the right.
...redirects the output of compiler.exe to an hypothetical small
messenger.exe utility whose unique task is to transform the output
into a list which can be polled at runtime via the Windows API.

I think the command should actually be

c:\compiler.exe sourcefilename -option | messenger.exe
Another solution, really simple, would be to dump the output to a file
and then read it from your interface:
c:\compiler.exe sourcefilename -option > dump.txt

Yes, AFA redirection to a file is concerned.

V
 
F

Francesco

Victor Bazarov ha scritto:
I believe you've confused two separate elements of the MS-DOS interface.
The '>' redirects the output of the left operand (executable) to the
file specified as the right operand, while '|' ("pipe") redirects the
output and makes it standard *input* of the executable on the right.


I think the command should actually be

c:\compiler.exe sourcefilename -option | messenger.exe

Oh, yes, I mistaken it, thank you for the correction.

In any case, that was just a pointer about a possible solution.

Francesco
 
R

Rüdiger Ranft

Leslaw said:
Hi,

I am writing a C++ program that does a job similar to a typical compiler:
It reads some source text, analyses it for errors, and performs some
conversion to a target format. I have a problem how to design the
communication between my code and user interfaces, to achieve
the following goals:

1) The code should be able to run either in the console mode, or
within a Windows GUI, but the kernel part of the code should be designed
in such a way so that it is independent on the interface.

You have (at least) two different solutions for this problem. You can
either create a console mode program, or put the compiler stuff into a
library. I choose to create a library such cases, with a small stub
executable for the console mode which calls the functions from the
library. The main advantage of this approach is that in the GUI the
errors from the compilation does not to be parsed, but can come from a
well specified interface. Also I use the strategy pattern[1] to notify
the GUI about the progress.

This is the outline of such a system:

class Progress
{
public:
// The function which the compiler calls when something changed
virtual void progress(int state) = 0;
// Errors callback
virtual void error(int line, std::string message) = 0;

// Always add a destructor to classes with virtual functions
~Progress();
};

class GuiProgress: public Progress
{
public:
virtual void progress( int state )
{
m_progressBar.setState( state );
}

virtual void error(int line, std::string message)
{
showDialog( message );
}
};

class GuiProgress: public Progress
{
public:
virtual void progress( int state )
{
std::cout << "progress: " << state / 100.0 << '%' << std::endl;
}

virtual void error(int line, std::string message)
{
std::cerr << "Error on line " << line << ": " << message << '\n';
}
};

class Compiler
{
public:
void compile( std::istream& input, Progress& pg );
};

bye
Rudi

[1] http://en.wikipedia.org/wiki/Strategy_pattern
 
J

Jorgen Grahn

Mildly offtopic, but I have to mention it anyway. I thought I had seen
all theoretically possible date formats already, but "8.09.2009" is
new to me. "8.9.2009" yes, but not a form with padded month numbers.
Hi Leslaw,
you can use the same technique used by GCC, that is, the compiler
itself is a console program that is called by passing the source
filename and the options to it, then it performs a single run dropping
all messages to the standard output stream.

When you have to call it from a GUI interface, simply intercept its
output and display it on your interface.

Actually, that compiler is not interactive, and I believe it is the
best technique - after all, once it intercepts a problem in the source
you have either to change the source or change its parameters, and
then the best solution is to restart it from scratch with the new
input.

Yes. All this is how compilers and compiler-like programs (not just
GCC) have worked for the past forty years.

Actually it would surprise me if even Windows compilers were
implemented in any other way. It has too many benefits. (One is that
you can implement the compiler in portable C++.)

This is one of the many things that the solution above makes non-issues.
It's even trivial to make the GUI thing call a standard make tool instead
of your "compiler", so the user can e.g. build a dozen things in
parallel to speed things up on an SMP machine. No code needed.
Your GUI can simply display the messages in a clickable list and
"decode" each error message - which should report the filename and the
line of code where the error happened.

And if those error messages look even /remotely/ like those from other
"compilers", I bet existing GUIs know what to do with them. I know
Emacs (M-x compile) does.

/Jorgen
 
J

James Kanze

Just for the record, I've never seen a compiler which had a
graphical interface.
I believe you've confused two separate elements of the MS-DOS
interface. The '>' redirects the output of the left operand
(executable) to the file specified as the right operand, while
'|' ("pipe") redirects the output and makes it standard
*input* of the executable on the right.

The usual solution is for the GUI (the IDE) to use something
like system() to invoke the compiler, redirecting the output to
a temporary file which it then reads and exploits, e.g.:

system( "c:\compiler.exe sourcefilename > c:\temp
\compiler.out" ) ;
I think the command should actually be
c:\compiler.exe sourcefilename -option | messenger.exe

I doubt the that messenger.exe is even necessary; typically, the
IDE will start the compiler in a separate thread, which then
waits for it to finish using something like the wait() system
call under Unix. It then reads the compiler output files.
 
J

Joshua Maurice

I doubt the that messenger.exe is even necessary; typically, the
IDE will start the compiler in a separate thread, which then
waits for it to finish using something like the wait() system
call under Unix.  It then reads the compiler output files.

Slightly off-topic from thread's topic: Do people actually do such
complex and convoluted things like make a messenger program which uses
windows specific things and then poll from it? The first thing I do is
whip out my portable wrappers which work on posix and windows for
threading and processes. Ex:

ProcessBuilder pb = ProcessBuilder()
.exe("compiler.exe")
.arg("sourcefilename")
.arg("-option")
.redirectErrToOut()
.pipeOut();
auto_ptr<Process> p(pb.spawn());
auto_ptr<ProcessIstream> childsOut(
p->releaseReadEndFromChildsStdout());
for (string line; getline(*childsOut, line); )
cout << line << endl;

Boost is good too, for those shops which actually use it.
 
J

Jerry Coffin

[ ... ]
1) The code should be able to run either in the console mode, or
within a Windows GUI, but the kernel part of the code should be designed
in such a way so that it is independent on the interface.

That's certainly good so far.
2) The code should make available to the program user a list of
errors detected during its operation, with some additional information
about the location of the errors in the source text analysed.
Also, the user should be informed about the current status of
the program operation, by suitable messages.

Likewise, perfectly reasonable.
3) The operation of the code is rather fast, so that it seems that
it is not necessary to run it in a separate thread (but who knows
what will be needed in the future?)

Using multiple threads may make sense, even if it's not really
_necessary_. This does seem to contradict your second point though:
if the process takes long enough to run that the user will ever see
any message about its current status other than "finished", it's
taking long enough that it probably justifies running in a separate
thread (or process).

[ ... ]
In the case of console programs, the easiest thing to do is to send
various messages to an output stream, during the program operation, for example:

cout << "Error detected: missing right parenthesis".

However, this solution does not seem the best if the same code is to run
also under graphical GUI, because there is no direct way to connect the output
stream to the various windows.

Yes and no. If you use cerr directly, that's true. OTOH, it's fairly
easy to use an object of _some_ sort that can write to a stream or a
window, depending on what you prefer. For example, years ago I wrote
a program that had an ABC named 'display', with two descendants name
text_display and window_display. You could produce output via either
one (or, theoretically both). IIRC, if you instantiated more than one
window_display, each would create its own list-box to hold output. I
never really put that to much use, but in something like an IDE, I
could see using it to put output from different tools into different
windows.
It therefore seems that the interface-independent
kernel of my code should not send the messages but only keep a list of them somewhere.
The interface would later display the messages in this or other way. But this solution
is also not perfect, because then there would be a time shift between the actual
events and the information about the events, revealed to the user.

There's always going to be at least some delay between detecting an
error and displaying the associated message -- but as long as the
delay is less than 100 ms or so, the user won't be able to see the
difference. Even 200 ms still looks very close. In any case, a small
delay is rarely a huge problem. At very best, a typical screen is
only updated about 60 times a second, so trying to update faster than
that accomplishes nothing.

It's perfectly reasonable to queue a message in one thread, and have
a different thread update the display on the screen in response to
that message.
A related issue is how the code should behave internally when it detects
an error. Is this a good idea to use exceptions in such cases? If yes,
which part of the code (or interface?) should be responsible for
catching the exceptions?

Assuming your code really is a lot like a compiler, I'd say the
answer is probably no. An exception is for dealing with something
unexpected -- but compilers normally deal with human input, and
finding an error in human input is _never_ unexpected. In reality,
rather the opposite is true -- the _usual_ situation is for a
compiler to find at least one error, and issue a message about it. In
most cases, such an error means no other output is created, so the
majority of the time, the ONLY output from a compiler is error
messages. Once the compiler runs successfully and generates code as
output, there's not a great deal of reason to run it on that code
again (except for things like installing a package from source code).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top