The content of this blog is my personal opinion only. Although I am an employee - currently of Nvidia, in the past of other companies such as Iagination Technologies, MIPS, Intellectual Ventures, Intel, AMD, Motorola, and Gould - I reveal this only so that the reader may account for any possible bias I may have towards my employer's products. The statements I make here in no way represent my employer's position, nor am I authorized to speak on behalf of my employer. In fact, this posting may not even represent my personal opinion, since occasionally I play devil's advocate.

See http://docs.google.com/View?id=dcxddbtr_23cg5thdfj for photo credits.

Tuesday, June 16, 2009

Error reporting in libraries

It's a perennial problem: You have written a library dfunction, perhaps a header-only class. Usually it is silent. However, occasionally it suffers errors or warnings. The interface does not provide a nice way to return an error code - e.g. you are trying to return a string, not an int where 0 or -1 indicates failure. You want to print error messages or otherwise report errors for these hopefully rare cases.

Special case: assertion failures. If you are writing something like a simulator, with simulator stdout and stderr separate from program-under-simulation stdout or stderr, you probaby want to ensure your assertion failures go the the simulator log. This is usually easy, but in Pin simulator and program file stdout and stderr are shared.

Note: we are not just talking about error messages that are followed by death. Sometikes we are talking about warning or informational messages, after which the program should keep running.

if everything belongs to a single class, you might give the class daa members for the streams to be used. e.g.

class Foo {
std::ostream m_cout;
std::ostream m_cerr;
std::ostream& my_cout();
std::ostream& my_cerr();

with the usual methods wrapping the data member ostreams.
E.g. to allow you to print to std::cout and std::cerr if not initialized.
But not everything lies in the same object/class.

Passing around ostreams to every possible function is ugly.

Adding ostreams to every possible class is ugly.

I've occasionally passed generic "env" objects to lots of functions, where the env carries things like ostreams, and other good stuff like exit functions.
But this is ugly.

My BKM is beginning to look like ... Cpp (C preprocessor) macros COUT and CERR used everywhere. With COUT defined tio be std::cout, or my_cout(), or whatever.

Formalized as a header file COUT_CERR_ostream_redirection.hpp:

// Interface:
// To use std::cout and std::cerr
// #define USE_STD_COUT 1
// #include "COUT_CERR_ostream_redirection.hpp"
// This is also the default - if neither USE_STD_COUT and USE_MY_COUT_FUNCTIONS are defined
// To use my_cout() and my_cerr()
// #include "COUT_CERR_ostream_redirection.hpp"
// To use neither
// #define USE_STD_COUT 0
// #include "COUT_CERR_ostream_redirection.hpp"
// If you want to #include this header multiple times
// - equivalent to doing a #undef COUT_CERR_OSTREAM_REDIRECTION_already_included

#undef COUT_CERR_OSTREAM_REDIRECTION_already_included

#ifndef COUT_CERR_OSTREAM_REDIRECTION_already_included
#define COUT_CERR_OSTREAM_REDIRECTION_already_included

#if !defined(USE_STD_COUT) && !defined(USE_MY_COUT_FUNCTIONS)
#define USE_STD_COUT 1

#define COUT std::cout
#define CERR std::cerr
extern std::ostream& my_cout();
extern std::ostream& my_cerr();
#define COUT my_cerr()
#define CERR my_cout()
// let user define his or her own COUT and CERR macros
// e.g. #define COUT foo_cout() in module foo, and bar_cout() in module_bar()

#endif //COUT_CERR_OSTREAM_REDIRECTION_already_included