ObjectStore does not support the use of the C++ exception handling for error conditions that occur during an ObjectStore operation. Instead, ObjectStore provides the TIX exception-handling facility, which includes an error-reporting mechanism and services for handling ObjectStore exceptions.
The predefined exceptions are organized into groups. Each group has a parent exception; the exceptions within the group are referred to as child exceptions. Parent exceptions can be used where you would use child exceptions, but they are most useful when specified in a TIX exception handler to catch a group of child exceptions; see Handling Multiple Exceptions.
DEFINE_EXCEPTION(name, "message", pointer-to-parent ); DECLARE_EXCEPTION(name );The DEFINE_EXCEPTION macro performs the definition and the DECLARE_EXCEPTION macro makes the definition available to other program files. Both macros must end in a semicolon (;).
message is the error message to display when the exception is signaled. As shown in the syntax statement, this argument must be enclosed in quotation marks.
pointer-to-parent is the address of a parent exception, which thus becomes the new exception's parent. For information about parent exceptions, see Predefined Exceptions. Supply 0 for this argument if you do not want your exception to have a parent.
Defining and declaring
If you use the exception in only one file, you can dispense with the DECLARE_EXCEPTION macro altogether and just include the DEFINE_EXCEPTION macro in the program file that uses the exception. Otherwise, include DECLARE_EXCEPTION in the definitions file, and DEFINE_EXCEPTION in a header file that is included by program files that want to use the exception. Signaling
Predefined TIX exceptions are signaled internally by ObjectStore. How do you signal a user-defined exception? It is triggered explicitly by the user application when it detects the condition for which the exception was defined. User-defined (as well as predefined) exceptions are instances of the class objectstore_exception, which gives them access to the function objectstore_exception::signal(). When you call this function, specify the exception identifier as the implicit this argument; the effect is to signal the exception.
Example
The example program discussed in Chapter 2, Anatomy of an ObjectStore Application shows defining, declaring, and using a user-defined TIX exception. Following is a summary of the process.
The exception is defined in the definitions file, bookclub.cc.
DEFINE_EXCEPTION(err_no_entry_point,
"No entry-point object for", 0);
To make the exception err_no_entry_point available to other parts of the program, it is declared in the header file, bookclub.hh, line 10:
DECLARE_EXCEPTION(err_no_entry_point);One of the program files that includes bookclub.hh is the main application file, main.cc, which also uses the exception. In lines 38-39, an if statement verifies that the return value from get_value() was nonzero, indicating that the entry-point object was retrieved; see Retrieving the Entry-Point Object. If get_value() returns 0 (no entry-point object was retrieved), the program calls signal(), as follows:
err_no_entry_point.signal(
"%s - use os_database_root::set_value()", argv[1]);
This call to signal() triggers the exception. signal() also performs printf-like formatting for displaying additional information about the error condition; see Signaling Exceptions. In this example, the exception is not handled and the program aborts. However, you can handle user-defined as well as predefined exceptions in a TIX exception handler, as described in Establishing a TIX Exception Handler.
Establishing a TIX Exception Handler
ObjectStore provides four macros for establishing TIX exception handlers:
TIX_HANDLE (exception) TIX_HANDLE_IF (flag, exception) TIX_EXCEPTION TIX_END_HANDLEexception is the name of the exception you want to handle. It can be one of the following:
An exception handler generally requires two statement blocks, the block in which the exception can occur and the block that actually handles the exception. When establishing a TIX exception handler, use TIX_HANDLE and TIX_END_HANDLE to enclose both blocks and TIX_EXCEPTION to separate them:
TIX_HANDLE(exception) {
// exception block
} TIX_EXCEPTION {
// handler block
} TIX_END_HANDLE
The braces enclosing the statement blocks are not required, but some source editors might complain if you do not use them. If the exception is signaled during execution of the statements in the exception block, control passes from the statement that caused the exception to the first statement in the handler block. Otherwise, control passes from the last statement in the exception block to the first statement following TIX_END_HANDLE. For a simple example, see the exception handler used in main.cc, lines 20-28, listed in An Example Application.
If the specified exception is signaled but there is no handler block, control passes directly to the first statement following the TIX_END_HANDLE macro, as if the exception were handled. See Handling Exceptions Outside a Handler.
TIX_HANDLE(exception1) {
TIX_HANDLE(exception2) {
. . .
TIX_HANDLE(exceptionN) {
// exception block
} TIX_EXCEPTION {
// handler block for exceptionN
} TIX_END_HANDLE
} TIX_EXCEPTION {
// handler block for exception2
} TIX_END_HANDLE
} TIX_EXCEPTION {
// handler block for exception1
} TIX_END_HANDLE
You can also handle an entire group of exceptions. As explained in Predefined Exceptions, the TIX exceptions are organized in groups by the parent exception. To handle a family of exceptions, specify the name of the parent in the TIX_HANDLE macro, as follows:
TIX_HANDLE(parent-exception) {
// exception block
} TIX_EXCEPTION {
// handler block for all child exceptions under parent-exception
} TIX_END_HANDLE
You might want to handle some child exceptions specifically and others generically. To do so, create a nested exception handler, and specify the parent exception in the outer nest and each of the child exceptions you want to handle in the inner nests, as follows:
TIX_HANDLE(parent-exception) {
TIX_HANDLE(child-exception1) {
TIX_HANDLE(child-exception2) {
. . .
TIX_HANDLE(child-exceptionN) {
// exception block
} TIX_EXCEPTION {
// handler block for child-exceptionN
} END_TIX_HANDLE
} TIX_EXCEPTION {
// handler block for child-exception2
} TIX_END_HANDLE
} TIX_EXCEPTION {
// handler block for child-exception1
} TIX_END_HANDLE
} TIX_EXCEPTION {
// handler block for all other children of parent-exception
} TIX_END_HANDLE
#include <ostore/ostore.hh>
#include <fstream.h>
main(int argc, char **argv)
{
objectstore::initialize();
TIX_HANDLE (err_objectstore) { // parent exception
// the next two exceptions are descended from err_objectstore
TIX_HANDLE (err_database_not_found) { // bad name
TIX_HANDLE (err_invalid_pathname) { // no argument (null)
os_database* db = os_database::open(argv[1]);
db->close();
cout << "The database named " << argv[1] << " exists.\n";
} TIX_EXCEPTION {
cout << "Usage: " << argv[0] << " <database name>\n";
} TIX_END_HANDLE
} TIX_EXCEPTION {
cout << "Sorry, no such database. Try again.\n";
} TIX_END_HANDLE
} TIX_EXCEPTION {
// get a full report on the exception
cout << tix_handler::get_report();
} TIX_END_HANDLE
}
Following is the output from a sample run:
$tix_nestUsage: tix_nest <database name>$tix_nest stuffSorry, no such database. Try again.$tix_nest real_dbThe database named real_db exists.
As mentioned in Establishing a TIX Exception Handler, the handler block can be omitted from a TIX exception handler. If an exception occurs in a handler that does not have a handler block, program control passes from the statement in the exception block that caused the exception to the first statement outside the handler. This behavior effectively clears the exception.
To illustrate, consider a handler that prompts the user for a file name if the application fails to open either of two database files with the supplied names. The handler can never prompt for the names of both files, even if the names are both wrong. The reason is that the exception generated by the failed attempt to open the first file would prevent the application from trying to open the second file.
Example
The following example program illustrates this type of exception handler:
// no_hndlr.cc: Uses a handler-less TIX exception handler
// to handle exception signaled if one or the other or both of the
// databases specified on command line do not exist
#include <ostore/ostore.hh>
#include <fstream.h>
main(int argc, char** argv)
{
OS_ESTABLISH_FAULT_HANDLER {
if(argc!=3) { // check for two command-line arguments
cout << "Usage: " << argv[0] << " <db1> <db2>\n";
return 1;
}
objectstore::initialize();
// set both os_database pointers to 0 so we can check later to
// see which database we were able to open, if any
os_database *db1 = 0, *db2 = 0;
TIX_HANDLE (err_database_not_found) {
db1 = os_database::open(argv[1]); // open first db
} TIX_EXCEPTION {
// no handler code
} TIX_END_HANDLE
TIX_HANDLE (err_database_not_found) {
db2 = os_database::open(argv[2]); // open second db
} TIX_EXCEPTION {
// no handler code
} TIX_END_HANDLE
// check if files were opened; null means the open failed
if (!db1 || !db2) {
cerr << "Cannot open ";
if (db2)
cerr << argv[1]; // failed to open first db
else if (db1)
cerr << argv[2]; // failed to open second db
else // couldn't open either one
cerr << argv[1] << " or " << argv[2];
cerr << endl;
return 1;
}
else // both databases exist
cout << "Opened " << argv[1] << " and " << argv[2] << endl;
} OS_END_FAULT_HANDLER
return 0;
}
Following is the output from a sample run; it assumes that that no_db1 and no_db2 do not exist and that real_db1 and real_db2 do exist:
$no_hndlr no_db1 real_db2 Cannot open no_db1$no_hndlr real_db1 no_db2 Cannot open no_db2$no_hndlr no_db1 no_db2 Cannot open no_db1 or no_db2
basic_undo objectstore_exception os_lock_timeout_exception tix_context tix_exception tix_handlerThese classes fill out the functionality of TIX exception handling, providing services such as explicit signal control, debugging tools, and error-message formatting. The following sections describe the more commonly used of these features.
For detailed information about the TIX classes and their member functions, see the following in the C++ API Reference:
The signal() function is useful when the application can detect an error condition before ObjectStore can. Instead of performing superfluous processing, it signals the exception. For example, an application can detect that a string that holds the name of a database is empty. Instead of trying to open the file, it can signal err_invalid_pathname or any other appropriate exception, as follows:
if (!*name)
err_invalid_pathname.signal("The string you supplied for
filename is empty.");
and handle the exception. signal() also performs printf-like formatting for any additional messages you might want to display. signal() thus enables you to give more specific information about the exception and program state at the time of the exception.
The message is displayed only if the exception is not handled. If you call signal() inside a handler, you can supply an empty string ("") for the message argument.
For more information about signal(), see objectstore_exception::signal() in the C++ API Reference.
Displaying Additional Error Information
ObjectStore provides two classes for displaying additional information about an exception, tix_context and tix_handler. For more information about these classes, see the following in the C++ API Reference:
Consider, for example, the following declaration of tc_var:
{ // block A
. . .
{ // block B
tix_context tc_var("Hello, world\n");
. . .
{ // block C
. . .
}
}
}
The message Hello, world appears only if an exception occurs in block B. The message appears immediately after the message displayed by the exception. tix_context can be useful as a debugging tool, similar to the printf statement. That is, you can declare tix_context variables in different parts of your program that you suspect of causing an exception, so that a formatted message is displayed only if an exception occurs in the same scope.
For more information about get_report() and get_report0(), see the following in the C++ API Reference:
The following handler displays its own error message when it handles the exception err_database_not_found:
TIX_HANDLE (err_database_not_found) {
db1 = os_database::open(path);
} TIX_EXCEPTION {
// cerr << tix_handler::get_report();
cerr << "Bad file name\n";
} TIX_END_HANDLE
Following is the output:
Bad file nameAfter tix_handler::get_report() is uncommented, the handler displays the following:
The database was not found handcuff:/h/handcuff/2/jimmy/debug/test_tix/stuff (err_database_not_found) Bad file nameFor another example of how to use tix_handler::get_report(), see the example program listed in Handling Multiple Exceptions.
Updated: 03/09/99 13:54:54