POOL Example Programs


In some of the following examples we use an application base class (ExampleApp) to factorize out a set of common operations shared among most POOL example programs and provide basic services like a eg top-level exception handler. This does not mean that POOL requires this or in fact any base class for an application nor do we suggest to use this particular implementation for real applications. ExampleApp is just a minimal framework for the purpose of keeping the example code as short as possible.

Please also note that usually POOL will be integrated with an experiment framework and many of the operation performed in the example programs will later be performed rather under the control of that framework than explicitly by an POOL end user (eg decisions on the data clustering, transaction control, loading of relevant dictionaries).


Source code: SimpleWriter.cpp

The SimpleWriter example program is a minimal POOL application which creates a few persistent objects in a single POOL file. The best starting point for looking at the code is the ExampleApp::userMain() method which is re-implemented by each POOL example.

In the case of SimpleExample this method starts by loading an LCG Dictionary which contains the class definition for the two classes which are persisted in this program: ExampleClass and TNamed. ExampleClass is a rather simple type with a few elementary attributes and two references to TNamed objects. TNamed is the standard class as provided by ROOT - use to show that not only user types but also ROOT types can be persisted by POOL.

After defining the name of the output file (dbFile) which should not exist two placement objects are defined. The role of these objects is to determine in which POOL file and which POOL container in that file objects will later be stored. Please note that (since V1.0) we do not require any advance knowledge about the object type stored in an container. The placement hints in particular do not store any class information anymore. The information about the runtime type of objects stored is obtained directly from the user object when an object is (later) marked for writing.

At this point also the major and minor back end technology is specified. In the current release POOL support to store objects in ROOT (major technology) with either ROOT trees or ROOT directories as minor tenchnologies . The boolean useTree is used to select between these two minor technologies. Otherwise the user code interface is not affected when moving from one technology to another.

Note that defining the placement is not (yet) changing the state of the referred file or container. It is purely defining a logical place in the storage hierarchy for later use in the program.

The next step in this example is to start an update transaction against the a POOL store via the statement

As of V1.1 POOL opens individual database (file) connections and starts low level transactions implicitly as required. No explicit database connection or transaction handling by user code as in earlier release is required.

Once a transaction has been established object types known to the LCG dictionary can simply be instantiated and assigned to the POOL provided smart pointer (pool::Ref<>). Objects can than be made persistent by calling the mark_write method on the smart pointer object referring to them. At this time the placement hint is specifies which has been determined before. References between persistent objects can be setup simply by embedding smart pointers in persistent object and assigning the reference to another object to them.

In this example the instance of ExampleClass referred to by r1 will point to two TNamed objects referred to by r2 and r3.

Once objects have been created and the references between them have been setup the whole configuration will be written to the POOL database when the transaction specified in mark_write is committed via context()->transaction().commit(). At the same time also any changes to the file catalog are commited to the backend database or file and become visible to other potential users of the same POOL catalog.


Source code: DetailedWriter.cpp

DetailedWriter essentially corresponds to the SimpleWriter example above, except that it does not use the ExampleApp class and thus shows all the details required in order to make a simple object persistent using POOL.


Source code: SimpleReader.cpp

The SimpleReader program reads objects back from the POOL store which has been created by SimpleWriter.

Once the smart pointer (refObj) has been setup, reading the persistent ExampleClass object and the two TNamed revered from there is simply performed by dereferencing the smart pointer. The overloaded arrow operator on the POOL Ref<> smart pointer will call back to the file catalog, open the required file, and extract the object via the dictionary based conversion. So the use of all these POOL components is rather transparent to the reading program and does not require to specify any physical storage elements like file names or container names or object types. This information is kept internally by POOL and eg the involved file names and locations can be changed via the POOL file catalog tools without affecting reading applications.


Source code: DetailedReader.cpp

DetailedReader essentially corresponds to the SimpleReader example above, except that it does not use the ExampleApp class and thus shows all the details required in order to read back the simple object written by DetailedWriter.


Source code: TinyEventWriter.cpp

As of release V0.4 POOL supports more complex container classes as part of persistent objects - in particular C++ arrays, std::vector, std::list and std::string. This example show how to use these features to define a small event data model (see Examples/Libraries/TinyEventModelDict for details).

The program creates a persistent event header (event) and attaches separate persistent Tracker and Calorimeter objects which each embed std::vectors of Tracks/Clusters. It also shows how to setup 1-to-many associations using std::vector<Ref<Calorimeter> >.

Please note that in this example we also show how to physically stream different objects into different databases (root files) without losing the ability to cross reference between those objects. Each category of objects (event header, tracker objects and calo objects) are kept in an individual file.

Each event is entered into a MySQL object collection for later reference. Each object reference entered into the collection is annotated by some meta data - in this case just two numbers defining a run and event number. This meta data can later be used to select only a subset of objects from a larger collection - very similar to NTuple selections or the tag database approach.


Source code: TinyEventReader.cpp

The TinyEventReader example show how to read the different persistent objects back and prints some of the data content. The starting point for the navigation is the event collections created in TinyEventWriter.

Author(s): Dirk Duellmann