C++ API Reference

Chapter 4

System-Supplied Macros

This chapter provides reference information for all ObjectStore macros except those included with the collections facility. For information about those macros, see C++ Collections Guide and Reference. The descriptions are listed alphabetically by macro name. However, the macros can be grouped logically, as follows:

DECLARE_EXCEPTION()

Use this macro to declare a user-defined TIX exception that you have defined elsewhere in your application with the DEFINE_EXCEPTION() macro.

Syntax
DECLARE_EXCEPTION(name); 
name is the name of a user-defined TIX exception you want to declare. It must be defined in the same application, using the DEFINE_EXCEPTION() macro. A user-defined TIX exception is a variable of the class objectstore_exception and is part of the TIX exception-handling facility.

Note
The DECLARE_EXCEPTION() macro must end with a semicolon (;).

The DECLARE_EXCEPTION() macro makes a TIX variable available to files other than the one in which it is defined. For example, by specifying the DECLARE_EXCEPTION() macro in a header file, you make the declared variable accessible to any file that includes the header file.

Example
The following example uses the DECLARE_EXCEPTION() macro to declare the variable bad_char_input that is defined elsewhere in the program with the DEFINE_EXCEPTION() macro:

DECLARE_EXCEPTION(bad_char_input) 
For more information
For information about

DEFINE_EXCEPTION()

Use this macro to define your own TIX exception. A user-defined TIX exception is a variable of the class objectstore_exception and is part of the TIX exception-handling facility.

Syntax
DEFINE_EXCEPTION(name, "message", pointer-to-parent); 
name is the identifier for the exception you want to create.

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 Parent Exceptions. Supply 0 for this argument if you do not want your exception to have a parent.

Note
The DEFINE_EXCEPTION() macro must end in a semicolon (;).

Example
The following program shows how to use the DEFINE_EXCEPTION() macro. The program defines three exceptions, err_too_few_args, err_too_many_args, and err_bad_arg. These are used to perform error-checking on command-line arguments. The first one, err_too_few_args, is handled by a TIX exception handler; the other two are not handled. All three exceptions are signaled by calls to the method signal(); see objectstore_exception::signal().

// def_exc.cc:  Illustrates how to use the DEFINE_EXCEPTION 
// macro to define your own TIX exceptions. This program defines 
// TIX exceptions to handle command-line argument errors. The 
// program signals err_too_few_args if there are no arguments 
// present, err_too_many_args if there are more than one, 
// and err_bad_arg if the word "bad" is used as an argument. 
// Only err_too_few_args is handled. 
#include <ostore/ostore.hh> 
#include <string.h> 
#include <fstream.h> 
DEFINE_EXCEPTION(err_too_few_args, "Too few arguments", 0);
DEFINE_EXCEPTION(err_too_many_args, "Too many arguments", 
0);
DEFINE_EXCEPTION(err_bad_arg, "Illegal argument: ", 0);
void validate_args(int argc, char **argv);
main(int argc, char **argv)
{
      OS_ESTABLISH_FAULT_HANDLER {
            objectstore::initialize();
            TIX_HANDLE (err_too_few_args) {
                  validate_args(argc, argv);
            } TIX_EXCEPTION {
                  tix_exception *exception = tix_handler::get_exception();
                  if (exception == &err_too_few_args)
                        cout << "Missing command-line arguments.\n"; 
                  else
                        cout << "Unknown exception occurred.\n";      
            } TIX_END_HANDLE
      } OS_END_FAULT_HANDLER
}
void validate_args(int argc, char **argv)
{
      if (argc == 1)  // no arguments
            // No message is specified because the default message 
            // specified in the DEFINE_EXCEPTION macro is sufficient. 
            err_too_few_args.signal("");
      else if (argc > 2) // more than one argument
            // No message is specified, so only the default message
            // specified with the DEFINE_EXCEPTION macro will
            // be displayed.
            err_too_many_args.signal("");
      else if (argc == 2 && !strcmp(argv[1], "bad"))
            // This call to signal includes a message,
            // which will therefore be displayed along with the
            // default message specified with the DEFINE_EXCEPTION
            // macro.
            err_bad_arg.signal(argv[1]);
}
Following is the output from a sample run:

$ def_exc 
Missing command-line arguments. 
$ def_exc arg 
$ def_exc arg arg 
Too many arguments 
(err_too_many_args) 
$ def_exc bad 
Illegal argument: 
bad (err_bad_arg) 
Available on line
This example program is available on line in

For more information
For information about

OS_BEGIN_TXN()

OS_BEGIN_TXN() begins a lexical transaction and establishes a new scope; OS_END_TXN() ends and commits the transaction started by the previous matching OS_BEGIN_TXN() call. OS_END_TXN() also marks the end of the scope established by OS_BEGIN_TXN().

Note
ObjectStore also supports dynamic transactions, which use member functions of the class os_transaction; see os_transaction.

OS_BEGIN_TXN_NAMED() is identical in behavior to OS_BEGIN_TXN(), except that it allows you to specify a transaction name. The name must be unique among transactions in the same function. It is used in constructing statement labels.

Syntax
OS_BEGIN_TXN(txn-tag,expression,txn-type) { 
      . . . 
} OS_END_TXN(txn-tag) 
or

OS_BEGIN_TXN_NAMED(txn-tag,expression,txn-type,txn-name) { 
      . . . 
} OS_END_TXN(txn-tag) 
There must be no spaces anywhere in the argument lists to OS_BEGIN_TXN and OS_BEGIN_TXN_NAMED. Also, the enclosing braces are not required, but some source code editors complain if you do not use them.

Arguments
txn-tag is an identifier - a tag for the transaction that is not used for any other transaction in the same function. (The tags are used to construct statement labels and must therefore have the same scope as labels in C++.) The tag specified in a transaction's OS_BEGIN_TXN() and OS_END_TXN() macros must be the same.

expression is of type tix_exception** and specifies a location in which ObjectStore stores the transaction's completion status. If the tix_exception** points to 0 when the transaction completes, the transaction committed; that is, its changes were made permanent and visible.

If the tix_exception** points to a nonzero value, the transaction aborted; that is, any changes to persistent state within the transaction were undone. The identity of the exception gives the reason for the abort.

In the event of an abort that causes the transaction to abort, you can examine expression only if one of the following is true:

The referenced exception in expression can be one of the predefined exceptions listed in Chapter 5, Predefined TIX Exceptions or a user-defined exception. See DEFINE_EXCEPTION() for information about user-defined exceptions.

txn-type is one of the following enumerators:

For more information about these enumerators, see os_transaction::read_only and os_transaction::update.

txn-name is the name you want to give to the transaction. See os_transaction::get_name().

Nesting
Transactions can be nested. For example, you can nest an update transaction inside a read-only transaction if you want to write temporarily to persistent memory, as in the following:

OS_BEGIN_TXN(txn1, 0, os_transaction::read_only) { 
      OS_BEGIN_TXN(txn2, 0, os_transaction::update) { 
            // process database 
            os_transaction::abort(); // Roll back the transaction 
      } OS_END_TXN(txn2) 
} OS_END_TXN(txn1) 
Example
The following example program illustrates how to use the OS_BEGIN_TXN() and OS_END_TXN() macros to form a lexical transaction. By way of contrast, the program also illustrates a dynamic transaction.

The program creates a database (stanza.db) and uses a lexical transaction to write an array of strings to the database. After the lexical transaction commits, the program starts a dynamic transaction and reads the strings from the database, displaying them on standard output.

// txn_example.cc: illustrates lexical and dynamic transactions
// The program writes an array of strings to a database in a
// lexical transaction (using the macros), and reads the array
// in a dynamic transaction (using the os_transaction methods).
#include <ostore/ostore.hh>
#include <string.h>
#include <fstream.h>
#define SIZE 5
main( )
{
      os_database* db;
      os_database_root* root;
      os_transaction* txn;
      char *verse[] = {
            "Matter, as wise logicians say,",
            "  Cannot without a form subsist;",
            "And form, say I as well as they,",
            "  Must fail if matter brings no grist.",
            ""
      };
      OS_ESTABLISH_FAULT_HANDLER {
            objectstore::initialize();
            // create the database "stanza.db"; if it already exists, 
            // overwrite it
            db = os_database::create("stanza.db", 0644, 1);
      
            // start a lexical transaction in update mode
            OS_BEGIN_TXN(t1, 0, os_transaction::update) {
                  // allocate persistent storage for an array of pointers to 
                  // chars
                  char **s = (char**)new(db,
                        os_typespec::get_pointer(), SIZE) void**[SIZE];
                  // create a root ... 
                  root = db->create_root("root");
                  // ... and associate it with the first element of the array,
                  // making it the entry point
                  root->set_value(s, os_typespec::get_pointer());
                  for (int i = 0; i < SIZE; i++) {
                        int len = strlen(verse[i])+1;
                        // allocate persistent storage for the string, clustering
                        // it with the previously allocated array
                        s[i] = new(os_cluster::with(s),
                              os_typespec::get_char(), len) char[len];
                        // copy the string in verse[i] to the allocated storage
                        strcpy(s[i], verse[i]);
                  }
            } OS_END_TXN(t1)
            db->close(); // close database
            db->open(0); // re-open database for read-only
            // start a dynamic transaction in read-only mode
            txn = os_transaction::begin(os_transaction::read_only);
            root = db->find_root("root"); // find the database root
            // get the entry-point (the first element of an array of
            // pointers to chars) associated with the root
            char** s = 
                  (char**)(root->get_value(os_typespec::get_pointer()));
            // print the strings
            for (; **s; s++)
                  cout << *s << endl;
            txn->commit(); // commit transaction
            db->close(); // close database
            // clean up
            delete db;
            delete root;
            delete txn;
      } OS_END_FAULT_HANDLER
      return 0;
}
Following is the output from a sample run:

$ txn_example 
Matter, as wise logicians say, 
      Cannot without a form subsist; 
And form, say I as well as they, 
      Must fail if matter brings no grist. 
Available on line
This example program is available on line in

For more information
For information about using the OS_BEGIN_TXN() and OS_END_TXN() macros to write lexical transactions, see Using Lexical Transactions in Chapter 5 of the C++ API User Guide.

OS_BEGIN_TXN_NAMED()

Like the OS_BEGIN_TXN() macro, this macro begins a lexical transaction and establishes a new scope. Unlike the other macro, this one allows you to specify a transaction name.

For more information
See OS_BEGIN_TXN() for a full description of the transaction macros.

OS_END_FAULT_HANDLER

Used with the OS_ESTABLISH_FAULT_HANDLER macro to delimit a fault-handler block.

For more information
See OS_ESTABLISH_FAULT_HANDLER for information on the fault-handler macros.

OS_END_TXN()

Marks the end of a lexical transaction established by the OS_BEGIN_TXN() or OS_BEGIN_TXN_NAMED() macro.

For more information
See OS_BEGIN_TXN() for a full description of the lexical transaction macros.

OS_ESTABLISH_FAULT_HANDLER

The OS_ESTABLISH_FAULT_HANDLER and OS_END_FAULT_HANDLER macros are used to handle page faults. ObjectStore uses page faulting to detect references to persistent memory. All applications performing ObjectStore operations must use these macros to establish a page-fault handler at the top of every stack in the program. Essentially, this requirement means that you must enclose the code in the top-level function (main() or WinMain()) within these macros. If the program uses multiple threads, the first function of each new thread that uses ObjectStore must also include these macros.

Syntax
int main (int argc, char** argv)
{
      OS_ESTABLISH_FAULT_HANDLER { 
            // your code 
      } OS_END_FAULT_HANDLER 
      return value; 
} 
The enclosing braces are not required but some source code editors complain if you do not use them.

For more information
See Establishing a Page Fault Handler in Chapter 3 of the C++ API User Guide for information about using the fault-handler macros.

OS_MARK_SCHEMA_TYPE()

This macro is used in schema source files to include a class in an application's schema.

Syntax
OS_MARK_SCHEMA_TYPE(class)
class is the class to be included in the application's schema. Typedef names are not allowed. To include a template class that has more than one argument, use the OS_MARK_SCHEMA_TYPESPEC() macro.

Calls to the macro should appear outside any function at global or file scope level.

Required include files
Schema source files must include the file <ostore/manschem.hh> after including <ostore/ostore.hh> and should contain all the include lines from the application's source necessary to compile.

You must mark every class on which the application might perform persistent new, as well as every class whose instances can be used by the application as database entry points. You must also mark every class that appears in a library interface query string or index path in the application.

However, if you specify the -make_reachable_source_classes_persistent option to the ossg schema generator, you need not mark every class that might be read from a database. This option causes every class that is defined in the schema source file and reachable from a persistently marked class to be persistently allocatable and accessible. The -make_reachable_source_classes_persistent option is useful to ensure that items that could be overlooked, for example, subtypes, are marked. However, it can also make classes persistent that do not need to be so for compilation to succeed.

For more information
For information about

OS_MARK_SCHEMA_TYPESPEC()

This macro is used in schema source files to include a class template in an application's schema when the template has more than one argument.

Syntax
OS_MARK_SCHEMA_TYPESPEC((type-name<param-list>)) 
type-name is the name of a class template, and param-list is a comma-separated list of parameters.

You can use this macro to parameterize types with multiple parameters. Note that you must enclose type-name and param-list within a set of parentheses. If the template has one or no parameters, you can use the OS_MARK_SCHEMA_TYPE() macro.

Calls to the macro should appear outside any function at global or file scope level.

Required include files
Schema source files must include the file <ostore/manschem.hh> after including <ostore/ostore.hh> and should contain all the include lines from the application's source that are necessary to compile.

You must mark every class on which the application might perform persistent new, as well as every class whose instances can be used by the application as database entry points. You also must mark every class that appears in a library interface query string or index path in the application.

However, if you specify the -make_reachable_source_classes_persistent option to the ossg schema generator, you need not mark every class that might be read from a database. This option causes every class that is defined in the schema source file and reachable from a persistently marked class to be persistently allocatable and accessible. The -make_reachable_source_classes_persistent option is useful to ensure that items that could be overlooked, for example, subtypes, are marked. However, it can also make classes persistent that need be for compilation to succeed.

For more information
For information about

OS_REFERENCE_NOCLASS()

OS_REFERENCE_NOCLASS(type) 
If you use a reference type whose referent type is not a class, you must call this macro, passing the referent type. Calling this macro provides a definition for the template instantiation. For example, the following defines the class os_Reference<float>:

OS_REFERENCE_NOCLASS(float);
Implicit instantiation of the template defines operator ->(), which is invalid for nonclass parameters. Calling the macro defines an instantiation that does not provide operator ->().

For references whose referent type is not a class, you resolve the pointer by calling resolve() or by relying on the conversion operator.

For more information
For information about reference types, see os_Reference<T>.

OS_REFERENCE_PROTECTED_NOCLASS()

OS_REFERENCE_PROTECTED_NOCLASS(type) 
If you use a protected reference type whose referent type is not a class, you must call this macro, passing the referent type. Calling this macro provides a definition for the template instantiation. For example, the following defines the class os_Reference_protected<float>:

OS_REFERENCE_PROTECTED_NOCLASS(float);
Implicit instantiation of the template defines operator ->(), which is invalid for nonclass parameters. Calling the macro defines an instantiation that does not provide operator ->().

For references whose referent type is not a class, you resolve the pointer by calling resolve() or by relying on the conversion operator.

For more information
For information about protected reference types, see os_Reference_protected<T>.

OS_REPORT_DLL_LOAD_AND_UNLOAD()

This macro is used in schema source files when generating component schemas. It enables or disables automatic reporting of DLL loading and unloading.

Syntax
OS_REPORT_DLL_LOAD_AND_UNLOAD(boolean) 
When boolean is true, automatic reporting of DLL loading and unloading is enabled. This is accomplished by having ossg generate code that calls os_DLL_schema_info::DLL_loaded() and os_DLL_schema_info::DLL_unloaded() from the DLL's initialization and termination functions. By default, automatic reporting is enabled.

If boolean is false, automatic reporting is not enabled and you must write your own code to call os_DLL_schema_info::DLL_loaded() and os_DLL_schema_info::DLL_unloaded() from the DLL's initialization and termination functions.

For more information
For information about

OS_SCHEMA_DLL_ID()

This macro is used in schema source files when generating component schemas to specify the DLL identifier of the DLL.

Syntax
OS_SCHEMA_DLL_ID(string) 
This macro can be used multiple times, for example, to specify different platform-specific DLL identifiers. Do not conditionalize these calls on the platform; you want all the DLL identifiers to be recorded in any database that depends on this DLL.

You must call OS_SCHEMA_DLL_ID at least once in a DLL schema source file to distinguish it from an application schema.

For more information
For information about

OS_SCHEMA_INFO_NAME()

This macro must be used in schema source files when generating component schemas.

Syntax
OS_SCHEMA_INFO_NAME(name)
This macro generates the variable extern os_DLL_schema_info name; that is, the os_DLL_schema_info of this DLL. The default is to generate the schema information with a static name. Call this if you are going to call os_DLL_schema_info::DLL_loaded() yourself so you can get access to the os_DLL_schema_info.

This macro also works in an application schema, in which case the type of the variable is os_application_schema_info instead of os_DLL_schema_info.

For more information
For information about

os_soft_pointer

os_soft_pointer expands to os_soft_pointer32 on 32-bit platforms and os_soft_pointer64 on 64-bit platforms.

#define os_soft_pointer os_soft_pointer64
#define os_soft_pointer os_soft_pointer32
For more information
See os_soft_pointer32<T> and os_soft_pointer64<T> for more information.

OS_SOFT_POINTER32_NOCLASS()

If you use a 32-bit soft pointer type (see os_soft_pointer32<T>) whose referent type is not a class, you must call this macro, passing the referent type.

Syntax
OS_SOFT_POINTER32_NOCLASS(type)
type is the referent type. This provides a definition for the template instantiation. For example, the following defines the class os_soft_ptr32<float>:

OS_SOFT_POINTER32_NOCLASS(float);
Implicit instantiation of the template defines operator ->(), which is invalid for nonclass parameters. Calling the macro defines an instantiation that does not provide operator ->().

For soft pointers whose referent type is not a class, you resolve the pointer by calling resolve() or by relying on the conversion operator.

The ObjectStore API explicitly provides (using OS_SOFT_POINTER32_NOCLASS()) the following instantiations of os_soft_pointer32<T>:

OS_SOFT_POINTER64_NOCLASS()

If you use a 64-bit soft pointer type (see os_soft_pointer64<T>) whose referent type is not a class, you must call this macro, passing the referent type.

Syntax
OS_SOFT_POINTER64_NOCLASS(type)
type is the referent type. This provides a definition for the template instantiation. For example, the following defines the class os_soft_ptr64<float>:

OS_SOFT_POINTER64_NOCLASS(float);
Implicit instantiation of the template defines operator ->(), which is invalid for nonclass parameters. Calling the macro defines an instantiation that does not provide operator ->().

For soft pointers whose referent type is not a class, you resolve the pointer by calling resolve() or by relying on the conversion operator.

The ObjectStore API explicitly provides (using OS_SOFT_POINTER64_NOCLASS()) the following instantiations of os_soft_pointer64<T>:

TIX_END_HANDLE

This macro delimits the end of a block of code for which you have defined a TIX exception handler.

For more information
See TIX_HANDLE() for information about the TIX exception handler macros.

TIX_EXCEPTION

This macro is used in a block of code delimited by the macros TIX_HANDLE and TIX_END_HANDLE to mark the beginning of the statement block to which control is transferred in the event of a specified exception.

For more information
See TIX_HANDLE() for information about the TIX exception handler macros.

TIX_HANDLE()

This macro is used in conjunction with the macros TIX_EXCEPTION and TIX_END_HANDLE to specify a handler for an exception signaled by a given code block.

Syntax
TIX_HANDLE(exception) {
      exception-block 
} TIX_EXCEPTION { 
      handle-exception-block 
} TIX_END_HANDLE 
exception-block and handle-exception-block can consist of any number of statements and are considered to be within their own block. The enclosing braces are not required but many source code editors can complain if you do not use them.

exception can be one of the following:

If exception is signaled while control is dynamically within the exception-block, control is transferred to handle-exception-block, unless a more recently established handler for the exception is found first. (If the exception occurs within nested handlers, the innermost nest is considered the most recent.) After handle-exception-block executes, program control passes to the statement immediately following TIX_END_HANDLE.

If exception is signaled but there is no handle-exception-block, program control passes to the statement immediately following TIX_END_HANDLE. In this situation, you might want to handle the exception outside the TIX exception handler.

If exception is not signaled in exception-block, control flows from the end of exception-block to the statement immediately following TIX_END_HANDLE.

In place of the TIX_HANDLE() macro, you can substitute the TIX_HANDLE_IF() macro, which allows for conditional processing. See TIX_HANDLE_IF().

Nesting
These constructs can be nested, allowing handling of multiple exceptions. For example:

TIX_HANDLE(exception1) { 
      TIX_HANDLE(exception2) { 
            exception-block 
      } TIX_EXCEPTION { 
            handle-exception1-block 
      } TIX_END_HANDLE
} TIX_EXCEPTION { 
      handle-exception2-block 
} TIX_END_HANDLE 
Nested handlers are especially useful when the outer handler specifies a parent exception in exception1 and the inner handlers each specify a child of the parent in exception2. However, some care should be taken in constructing a nested handler to ensure that the parent is in the outer handler and the children are in the inner handlers. If they are reversed, the parent might never get control.

Example
The following program illustrates a simple TIX exception handler. The program tries to open the database file named on the command line. If the attempt fails because the database does not exist, ObjectStore signals err_database_not_found and program control passes to handle-exception-block, which handles the exception. The handler creates a database with the same name.

Note
The intent of this example (as of all the examples in this chapter) is to illustrate TIX exception handling and not to show how to open a database.

// tix_example.cpp: illustrates a very simple TIX exception handler.
// The program tries to open a database named on the command-line.
// If the open fails because the database doesn't exist 
// (err_database_not_found), the exception handler creates a 
// database of that name. 
#include <ostore/ostore.hh> 
#include <iostream.h> 
main(int argc, char** argv)
{
      OS_ESTABLISH_FAULT_HANDLER {
            if (argc != 2) { // check for name of database 
                  cout << "Usage:  " << argv[0] << " <database>" << endl;
                  return 1;
            }
            objectstore::initialize();
            os_database *db;
            TIX_HANDLE(err_database_not_found) { 
                  db = os_database::open(argv[1]); // try to open database 
            } TIX_EXCEPTION { // an exception was signaled 
                  cout << "Can't open " << argv[1];
                  cout << ", will create it." << endl;
                  db = os_database::create(argv[1]); 
            } TIX_END_HANDLE 
            cout << "Opened " << db->get_pathname() << endl;
            db->close(); // close database
      } OS_END_FAULT_HANDLER
      return 0;
}
Following is the output from running the program with different command-line arguments:

$ tix_example # no command-line arguments 
Usage:  tix_example <database> 
$ tix_example nonsense # one argument, but database doesn't exist
Can't open nonsense, will create it. 
Opened /h/handcuff/2/tmp/examples/nonsense 
$ tix_example nonsense # argument to an existing database 
Opened /h/handcuff/2/tmp/examples/nonsense 
Available on line
This example program is available on line in

For more information
See Establishing a TIX Exception Handler in Chapter 4 of the C++ API User Guide for more information about using TIX exception handlers.

TIX_HANDLE_IF()

The TIX_HANDLE_IF() macro is just like the TIX_HANDLE() macro except that it allows for conditional processing of the exception.

Syntax
TIX_HANDLE_IF(flag, exception) { 
      exception-block 
} TIX_EXCEPTION { 
      handle-exception-block 
} TIX_END_HANDLE 
The enclosing braces are not required but some source code editors complain if you do not use them.

flag is a conditional expression, as used in an if statement. If it evaluates to true (nonzero), the handler is established around exception-block; otherwise, no handler is established.

exception identifies the exception, just as it does in the TIX_HANDLE macro.

For more information
See TIX_HANDLE() for information about TIX exception handlers.



[previous] [next]

Copyright © 1999 Object Design, Inc. All rights reserved.

Updated: 03/15/99 16:44:26