The Support Kit: BArchivable

The Support Kit Table of Contents | The Support Kit Index

# BArchivable

Derived from: none

Declared in: be/support/Archivable.h

###### Summary

BArchivable is a protocol for archiving and unarchiving objects. When you archive an object, you record its state into a BMessage that can be sent to another application, flattened and saved as a file, cached in memory, and so on. Unarchiving does the opposite: It takes a BMessage archive and turns it into a functioning object.

### Archiving

To archive an object, you create a BMessage and pass it to the object's `Archive()` function:

       BMessage message;
       theObject->Archive(&message);

It's the job of the `Archive()` implementation to write a description of the object into the message. Each class is responsible for archiving the parts of the object that it defines. To incorporate properties archived by its base classes, it should begin its implementation of the function by calling the version of `Archive()` that it inherits from its base class.

The chain of calls to inherited functions ends at the BArchivable root class. Its version of `Archive()` puts the object's class name into the archive under the field named "class". This information is used later when instantiating objects from the archive. It's good practice for derived classes to put their own class names in the "class" array as well, provided that instances of the class can be initialized from the archive. An abstract class should not put its name in the array. (See `validate_instantiation()` for more on this issue.)

If a class doesn't have any data to add to the BMessage archive, it doesn't need to implement an `Archive()` function; it can rely on the version it inherits. However, it must implement the constructor and static `Instantiate()` function for its objects to be successfully unarchived.

#### Deep and Shallow Archives

`Archive()`'s second argument, a `bool` flag, indicates whether the archive should be deep or shallow. By default the flag is `true` (deep).

For a deep archive, a class should include in its archive any other objects that it "owns." For a shallow archive, it should exclude these objects. For example a BView object archives its children for a deep archive, but not for a shallow one.

To perform a deep archive, an object invokes `Archive()` on the objects it owns, and adds the resulting archives to its own archive. For example:

       status_t TheClass::Archive(BMessage *archive, bool deep)
       {
           baseClass::Archive(archive, deep);
           archive->AddString("class", "TheClass");
           . . .
           if ( deep ) {
               BMessage cronyArchive;
               if ( crony->Archive(&cronyArchive, deep) == B_OK )
                   archive->AddMessage("crony", &cronyArchive);
           }
       }

Neither a deep nor a shallow archive should include objects that the target object is associated with but doesn't own. For example, a BView doesn't archive its parent or the BWindow to which it's attached.

#### Names

Name collisions in an archive are not automatically detected and corrected. For example, if both class A and subclass B add fields named "dog", the unarchiving mechanism will get confused.

To try to avoid collisions, all the `Archive()` functions implemented in the Be kits use names beginning with an underbar ("_name") or the "be:" prefix ("be:name"). Use a different convention for naming archived data in the classes you define.

### Instantiability

To be unarchivable, a class must implement a constructor that takes a BMessage archive as an argument. The constructor is a counterpart to the `Archive()` function: it begins by calling the constructor for its immediate base class, and then looks in the BMessage for the fields that were added by its `Archive(`) function.

The class must also implement the static `Instantiate()` function (declared in BArchivable), which needn't do much more than call the archive-accepting constructor, and return a BArchivable pointer. For example:

       BArchivable *TheClass::Instantiate(BMessage *archive)
       {
           if ( validate_instantiation(archive, "TheClass"))
               return new TheClass(archive);
           return NULL;
       }

The `validate_instantiation()` function, provided by the Support Kit, is a safety check that makes sure the BMessage object is, in fact, an archive for the named class.

### Unarchiving

To unarchive a BMessage, you call the `instantiate_object()` function. When passed a BMessage archive, `instantiate_object()` looks for the first name in the "class" array, finds the `Instantiate()` function for that class, and calls it. Failing that, it picks another name from the "class" array (working up the inheritance hierarchy) and tries again.

`instantiate_object()` returns a BArchivable instance. You then use `cast_as()` to cast the object to a more interesting class. A typical unarchiving session looks something like this:

       /* archive is the BMessage that we want to turn into an object.
        * In this case, we want to turn it into a BView.
        */
       BArchivable *unarchived = instantiate_object(archive);
       if ( unarchived ) {
           BView *view = cast_as(unarchived, BView);
           if ( view ) {
               . . .
           }
       }

The BArchivable-derived class must be exported for unarchiving to work:

       class _EXPORT MyArchivableView : public BView {
          ...
          static BArchivable *Instantiate(BMessage *data);
          ...
       };

#### Dynamic Loading

As described so far, an application can only unarchive objects that it knows about—it can't unarchive an object that it doesn't have the code to run.

An additional convention gets around this restriction: A BMessage archive can include a `B_STRING_TYPE` field named "add-on" that contains the signature of an add-on that defines the archived object. If `instantiate_object()` fails to unarchive an object on its first try, it will look for the add-on image, load it, and try again.

It's not defined how a host will interact with an unarchived instance of a previously unknown class. It's up to the parties to define entry points and protocols, just as it is for any other add-on module.

## Constructor and Destructor

### BArchivable()

`BArchivable(`void`)`
`BArchivable(` BMessage *archive`)`

Does nothing.

### ~BArchivable()

virtual `~BArchivable() `

Does nothing.

## Static Functions

### Instantiate()

static BArchivable *`Instantiate(` BMessage *archive`)`

Derived classes should implement `Instantiate()` to return a new BArchivable object that was constructed from the BMessage archive. For example:

       BArchivable *TheClass::Instantiate(BMessage *archive)
       {
           if ( !validate_instantiation(archive, "TheClass") )
               return NULL;
           return new TheClass(archive);
       }

| `Instantiate()` must return a BArchivable *, regardless of the actual class in which it's implemented.

This function depends on a constructor that can initialize the new object from the archive BMessage. See "Instantiability", above, for more information.

The default implementation returns `NULL`.

## Member Functions

### Archive()

virtual status_t `Archive(` BMessage *archive, bool deep = true`)` const

The default implementation adds the name of the object's class to archive's "class" field. Derived classes must override `Archive()` to augment this implementation by adding, to the BMessage, data that describes the current state of the object. Each implementation of this function should begin by incorporating the inherited version:

       /* We'll assume that MyView inherits from BView. */
       status_t MyView::Archive(BMessage *archive, bool deep)
       {
           BView::Archive(archive, deep);
           . . .
       }

If the class can be instantiated directly from a derived class, it should also add its name to the "class" array:

       archive->AddString("class", "MyView");

The deep flag declares whether `Archive()` should include objects that "belong" to the archiving object. For example, a deep BView archive would include archived forms of the view's children. An example is given under "Deep and Shallow Archives", above.

`Archive()` should return `B_OK` if it's successful; otherwise, it should return `B_ERROR` or a more descriptive error code.

The Support Kit Table of Contents | The Support Kit Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..