A.3 Communicating with the User

A number of functions and classes are useful to mediate communicating information to the user. In addition to consolidating functionality like printing progress bars, hiding user communication behind a small API like the one here also permits easy modification of the communication mechanisms. For example, if pbrt were embedded in an application that had a graphical user interface, errors might be reported via a dialog box or a routine provided by the parent application. If printf() calls were strewn throughout the system, it would be more difficult to make the two systems work together well.

A.3.1 Error Reporting

pbrt provides four functions for reporting anomalous conditions. In order of increasing severity, they are Info(), Warning(), Error(), and Severe(). These functions are defined in the files core/error.h and core/error.cpp. All of them take a formatting string as their first argument and a variable number of additional arguments providing values for the format. The syntax is identical to that used by the printf family of functions. For example, if the variable rayNum has type int, then the following call could be made:

Info("Now tracing ray number %d", rayNum);

core/pbrt.h includes this header file, as these functions are useful to have available in almost all parts of the system.

<<Global Include Files>>+= 
#include "error.h"

We will not show the implementation of these functions here because they are a straightforward application of the C++ variable argument processing functions that in turn calls a common function to print the full error string. For sufficiently severe errors, the program aborts.

pbrt also has its own version of the standard assert() macro, named Assert(). It checks that the given expression’s value evaluates to true; if not, Severe() is called with information about the location of the assertion failure. Assert() is used for basic sanity checks where failure indicates little possibility of recovery. In general, assertions should be used to detect internal bugs in the code, not expected error conditions (such as invalid scene file input), because the message printed will likely be cryptic to anyone other than the developer.

<<Global Inline Functions>>+=  
#ifdef NDEBUG #define Assert(expr) ((void)0) #else #define Assert(expr) \ ((expr) ? (void)0 : \ Severe("Assertion \"%s\" failed in %s, line %d", \ #expr, __FILE__, __LINE__)) #endif // NDEBUG

A.3.2 Reporting Progress

The ProgressReporter class gives the user feedback about how much of a task has been completed and how much longer it is expected to take. For example, implementations of the various Integrator::Render() methods generally use a ProgressReporter to show rendering progress. The implementation prints a row of plus signs, the elapsed time, and the estimated remaining time. Its implementation is in the files core/progressreporter.h and core/progressreporter.cpp.

The constructor takes the total number of units of work to be done (e.g., the total number of camera rays that will be traced) and a short string describing the task being performed.

<<ProgressReporter Public Methods>>= 
ProgressReporter(int64_t totalWork, const std::string &title);

Once the ProgressReporter has been created, each call to its Update() method signifies that one unit of work has been completed. An optional integer value can be passed to indicate that multiple units have been done.

<<ProgressReporter Public Methods>>+=  
void Update(int64_t num = 1);

The ProgressReporter::Done() method should be called when all of the work being measured is complete; in turn, it lets the user know that the task is complete.

<<ProgressReporter Public Methods>>+= 
void Done();

A.3.3 Simple Float File Reader

A number of places in the pbrt code need to read text files that store a series of floating-point values. Examples include the code that reads measured spectral distribution data and the code that reads lens description files in Section 6.4. Both use the ReadFloatFile() function, which parses text files of whitespace-separated numbers, returning the values found in the given vector. The parsing code ignores all text after a hash mark (#) to the end of its line to allow comments.

bool ReadFloatFile(const char *filename, std::vector<Float> *values);