Taxonomic Modeling in C Based Object Ori

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 36

Taxonomic Modeling in

C++ Based Object-Oriented


Databases is Efficient
Wolfgang Kowarschick, Gerhard Köstler,
Werner Kießling

Report Nr. 320 1995


Taxonomic Modeling in C++ Based
Object-Oriented Databases is Efficient
Wolfgang Kowarschick1 Gerhard Köstler2
Werner Kießling2
1 2
Technische Universität München Universität Augsburg
Institut für Informatik Mathematisch-Naturwissenschaftliche Fakultät
Orleansstr. 34, D-81667 München Universitätsstr. 14, D-86135 Augsburg
kowa@informatik.tu-muenchen.de {koestler,kiessling}@informatik.uni-augsburg.de

Abstract

We investigate a modeling issue arising when object-oriented databases meet


object-oriented programming languages like C++. For the application modeling
process we argue that a taxonomic view is superior to the implementation view
popular in C++. As a consequence we require classes to have both types and
extensions. At the object definition level taxonomic modeling needs integrity
constraints in form of functional constraints. However, if implemented naively
taxonomic modeling may incur a considerable storage penalty. We present an
automatic storage optimization algorithm, which in practical cases achieves the
same low storage requirements as tailored C++ solutions. Our second focus
is on software reuse, which demands proper adaptation measures to ensure se-
mantically correct inheritance of update methods. We present a new adaptation
algorithm that can be carried out automatically at schema compile time. Fi-
nally we demonstrate how to rapidly implement taxonomic modeling on top of
a standard C++ based OODBS. Our results are exemplified throughout the
paper by practical CAD examples.

Keywords: object-oriented databases and modeling, taxonomic modeling and inher-


itance, functional integrity constraints, storage optimization, update method adapta-
tion, integration in C++, software reuse

1
1 Introduction
Object-oriented database systems (OODBS) exhibit several well-accepted virtues,
combining the strength of databases technology with the power and flexibility of
the object-oriented paradigm of programming languages like C++ or Smalltalk.
In particular, the rich type system of an OODBS, supporting the natural model-
ing of complex attributes, is one feature that attracted the attention of database
application builders. Object-orientation with its inheritance mechanism also sup-
ports software reuse ([Kim90, RBP+ 91, Boo94]), which is of major concern in soft-
ware engineering. It is conspicuous, however, that this capability has received
less attention in the OODBS community than other popular features. Indeed
this is not a coincidence, rather it is rooted in two different mentalities joining
together when C++ meets the OODBS-world: C++ programmers tend to have
an implementational view of the data modeling process leading to implementation
hierarchies ([ZM90]), whereas database folks tend to think in terms of extensions
or taxonomic modeling.
Let us give a motivating example to highlight the differences (cp. [LP91]).

Example 1.1 (Triangle World) Suppose that three kinds of triangles are to be
modeled: general triangles, right triangles, and equilateral triangles. In Figure 1
two different modeling views are expressed in OMT-notation ([RBP+91]). For
simplicity we only treat the shape of triangles but not their spatial location. On
the right side there is the C++ view that puts Triangle as subclass of Right-
Triangle. On the left side this model is turned upside down by defining Right-
Triangle and EquilateralTriangle as disjoint subclasses of Triangle, which reflects
a taxonomic, viz. extensional or database view. Expressions like A = B denote
integrity constraints. 

What are the benefits and drawbacks of each view of the world?
The C++ view: The structurally most simple class, EquilateralTriangle, is
placed at the top of the hierarchy. Attribute A represents all three sides of
such triangles. RightTriangle and Triangle are put below as subclasses according
to their additional attributes B and C.
This approach is guided by implementational considerations interpreting ISA-
links as structural inheritance. But this contradicts intuition, since it does not
capture that in reality the sets of right and equilateral triangles are disjoint.
Moreover, it is not possible to reuse the implementations of query method Circum-
ference and of update method Scale in subclasses: Circumference is implemented
as {return 3∗A} in√ EquilateralTriangle, but in RightTriangle it must be redefined
to {return A+B+ A2 + B2 }, and in Triangle to {return A+B+C}. On the other
hand, we have a very frugal use of storage. For each class only those attributes

2
Which view of the triangle world?

Database view C++ view

EquilateralTriangle
A: double
Circumference: double
ResizeA(double)
Scale(double)
Triangle
A,B,C: double
Circumference: double RightTriangle
ResizeA(double) B: double
ResizeB(double)
ResizeC(double) ResizeB(double)
Scale(double)

Triangle
RightTriangle EquilateralTriangle C: double
{C = A2+ B2} {A = B, B = C} ResizeC(double)

Figure 1: Taxonomy vs. implementation hierarchy

are defined that are “necessary” from an implementational point of view. More-
over, it is not possible to use update methods in a way causing anomalies, that
is, the implementation hierarchy can be considered as subtyping relationship in
the sense of [LP91]. For instance, it is not possible to construct an equilateral
triangle with different side lengths.
The database view: A different kind of object-oriented modeling is strictly
based on class taxonomies that correspond to classification hierarchies ([ZM90])
with a set inclusion semantics among class extents. We will denote this kind of
modeling by taxonomic modeling.
The taxonomy at the left side of Figure 1 represents a view that has been ac-
cepted by non-programmers for at least 2000 years: right and equilateral triangles
are special triangles, and there are no triangles which are both right and equi-
lateral. Class Triangle possesses the same methods as in the C++ hierarchy.
These methods are inherited by subclasses RightTriangle and EquilateralTriangle,
which in addition are restricted by functional constraints. The method Circum-
ference of class Triangle—implementable as {return A+B+C}—can be reused by
all subclasses without modification; overriding is, of course, possible.
Clearly, taxonomic modeling has a lot of conceptual advantages. But as the
trend is to couple C++ with OODBS technology, a new kind of impedance mis-
match might arise if this is not handled carefully. Technically this gap is man-
ifested by the fact that the class model in C++ ([Str91]) has no notion of an
extension, it only caters for types (“object factory” view, [ABD+ 89]), whereas

3
on the OODBS side there is the notion of types and extensions (“object ware-
house” view). Characteristically though, this is not even unanimously agreed
upon within the OODBS community itself; e.g., Versant ([VER94]) supports the
strict taxonomic view, whereas ObjectStore ([OSt94]) offers extensions decou-
pled from types. Even the ODMG-93 proposal ([Cat94]) supports both classes
with or without extensions. Since, as a matter of fact, a great deal of OODBS
applications are or will be written by C++-programmers, the storage efficiency
argument behind the implementational view of data modeling must be defeated.
In other words, the conceptual advantages of taxonomic modeling in C++ based
OODBS have to be provided without storage penalties.
The rest of the paper is organized as follows. In Section 2 we will promote more
thoroughly the case for taxonomic modeling. Section 3 presents our automatic
storage optimization algorithm based on the functional constraints used in tax-
onomic modeling. Thereafter, Section 4 deals with inheritance and automatic
adaptation of update methods. Section 5 describes how taxonomic modeling can
be implemented on top of a C++ based OODBS. Finally, Section 6 summarizes
our results and outlines areas of future work.

2 Taxonomic Modeling

2.1 Requirements of Taxonomic Modeling


In the area of object-oriented modeling there are many competing approaches,
e.g. [RBP+ 91, CY91, Boo94]. A good survey by [MP92] demonstrates that there
is little agreement on what functions are involved in object-oriented analysis and
design.
Since taxonomic modeling is based on a set inclusion semantics of the class ex-
tents, in a first step application-specific classes should be identified corresponding
to “sets of objects” as observed in the domain to be modeled. Next these classes
are structured according to the set inclusion relationships arising between them,
yielding the class taxonomy of our application. In a second step one should
identify attributes, methods, and integrity constraints.
As an important point for taxonomic modeling we set up the following postulates:

1. Physical data independence: The decision whether an attribute is really stored


by the OODBS or derived from other attributes will be left to a storage
optimizer. Thus the notion attribute does by no means imply that its value is
physically stored.

2. Integrity constraints are an integral component of taxonomic modeling.

4
Physical data independence has proved successful in the relational data model
separating the logical and physical database organization. It is likewise important
for object-oriented view definition ([AB91]).
The kind of integrity constraints √
we concentrate on here are functional constraints
between attributes, (e.g., C = A2 + B2 ), which have to be inherited just as
attributes and methods, and are used by our storage optimizer. Different kinds
of integrity constraints for object-oriented models, such as range constraints,
enumeration constraints, relational constraints, have been proposed (see, e.g.
[BCV91]).

2.2 A Sample CAD Application


One main thrust behind OODBS has been to cope with the deficiencies of re-
lational systems in non-standard applications arising in the areas of CAx, CIM,
OIS, and GIS. We claim that taxonomies are inherently characteristic for these
domains. As an example we examine a typical CAD application. Imagine a set of
architectural plans for a medium-size appartement complex, which contains the
familiar icons to represent doors, wash-basins, bathtubs, toilets, etc. This easily
ends up with graphical representations of hundred of thousands of “ellipse-typed”
objects which have to be managed by the OODBS.

Example 2.1 (CAD modeling) The left part of Figure 2 shows a part of the
taxonomy of a CAD application, which models ellipse arcs and specializations
(update and many query methods are left out for shortnes, see Figure 3 for
parameters of ellipse arcs).
The classes NonRotatedEllipseArc, Ellipse, and CircleArc are specified by functional
constraints. Moreover, Ellipse has an additional query method Area which only
makes sense for “closed” arcs. This method is inherited by NonRotatedEllipse and
Circle, which are non-disjoint subclasses of EllipseArc. In addition, Circle has an
attribute Radius used as an alias for the two coinciding semi-axes. 

This taxonomy as a natural model of the application domain promotes the reuse
of query and update methods; both method signatures and their implementations
can be reused (see methods ArcLength and Area). In some cases, however, it makes
sense to override inherited code; e.g., a complex and time consuming algorithm
is needed to implement ArcLength in class EllipseArc. Although this algorithm is
applicable to circle arcs as well, it should be overridden by the faster algorithm
{return 2 ∗ SemiMajorAxis ∗ π ∗ DeltaAngle} in class CircleArc.
The right part of Figure 2 depicts an implementation hierarchy for the CAD
classes introduced above. It was developed by an experienced C++ program-
mer as part of a large-scale CAD system. The main reason for this model is to

5
Taxonomic model C++ based model
EllipseArc
Center: Point
SemiMajorAxis: double Circle
SemiMinorAxis: double
StartAngle: double Center: Point
DeltaAngle: double Radius: double
RotateAngle: double Area: double
ArcLength: double ArcLength: double

NonRotatedEllipseArc Ellipse CircleArc NonRotatedEllipse


StartAngle: double SemiMinorAxis: double
Area: double DeltaAngle: double

{RotateAngle = 0} {StartAngle = 0} SemiMajorAxis: double


{DeltaAngle = 2*PI}

CircleArc NonRotatedEllipse NonRotatedEllipseArc Ellipse


Radius: double RotateAngle: double
{SemiMajorAxis=Radius,
SemiMinorAxis=Radius}

Circle EllipseArc

Figure 2: Taxonomic vs. C++ model for ellipse arcs

avoid the waste of secondary storage caused by assigning values to superfluous


attributes (e.g., SemiMinorAxis for circles) in a straightforward C++ implemen-
tation of the taxonomic model and is achieved by a clever structural inheritance.

Remark 2.2 Let us assume the following storage requirements: double requires
8 Byte, Point 16 Byte, and the OID 8 Byte. Then in the C++ based model of Fig-
ure 2 a Circle object occupies 32 Byte, in contrast to 72 Byte in the unoptimized
taxonomic model if all attributes were physically stored. 

We will demonstrate subsequently that the same storage efficiency can be gained
automatically for the taxonomic model. The implementation hierarchy, however,
has several severe drawbacks, e.g.:

• The hierarchy scrambles the natural semantics of the application objects.

• Method Area is inherited, e.g., by CircleArc, which does not make any sense.

6
SemiMajorAxis
SemiMinorAxis StartAngle
RotateAngle

X Axis
DeltaAngle

Figure 3: Parameters of ellipse arcs

• The implementations of methods Area and ArcLength of class Circle have to


be redefined in many subclasses.

Taxonomic modeling also applies to different application domains like CASE or


multimedia and one often encounters complex domains where subclasses are iden-
tified by means of integrity constraints. Thus integrity constraints are essential
in object-oriented data modeling and should not be substituted for by turning
the natural hierarchy upside down.

3 Storage Optimization

3.1 Basic Definitions


First we will clarify some basic terminology of object-oriented database systems
from our point of view.

Definition 3.1 (OODBS Notions) A class is an abstract data type with an


extent.1 The extent is a finite set of objects of the given type. Classes possess
attributes and different kinds of methods: constructor and destructor methods,
query methods, and update methods and are partially ordered according to the
set inclusion relationship amongst their extents forming a class taxonomy. Sub-
classes may have additional attributes and methods.2 Classes may have functional
constraints (FC) as introduced in Definition 3.2. Query methods and functional
constraints are inherited as usual by subclasses. Update methods are inherited
and adapted according to the mechanism discussed in Section 4. 
1
We are aware that this is a “touchy issue”, cf. [ABD+ 89].
2
That is, structural or behavioral refinement is not prohibited, if it coincides with taxonom-
ical requirements. Take managers as employees with a company car as canonical example.

7
Note that in implementation hierarchies only type constraints on method signa-
tures are inherited. The functional constraints needed for taxonomic modeling
are defined as follows.
Definition 3.2 (Functional Constraints) Let C be a class with attributes
A, let A, A1, . . . , An ∈ A be different attributes, and let fA be an n-ary function.
1. An equation fc ≡ A = fA (A1, . . . , An ) is called functional constraint (FC).
A is called head of fc (head(fc)), {A1, . . . , An } is the body of fc (body(fc)).
fA is called FC-function of fc (funct(fc)).
Let F be a set of FCs over A.
2. Let A, B ∈ A be two attributes. Then A directly depends on B, A ←d B, iff
there is an FC A = fA (X1 , B, X2 ) ∈ F , where X1 , X2 ⊆ A. The transitive
+
closure of this relation is denoted by ←d .
+
3. F is called acyclic, if ←d is an irreflexive partial ordering. 
Note that FCs with an empty body (n = 0) cover constant constraints like
RotateAngle = 0.

3.2 The Optimization Algorithm


Storage optimization has always been a concern for OODBS (see e.g. [Wil91]).
Now we present an algorithm performing a storage optimization based on the
functional constraints of the taxonomic model. Functional constraints A = fA (X)
imply functional dependencies X → A between class attributes. The basic idea of
this algorithm relies on an application of relational normalization theory ([Ull88]).
The algorithm returns two disjoint sets of attributes—Astore that are physically
stored in the database and Acomp that are computed from Astore—as well as a set
Fcomp of functional constraints determining how to compute Acomp attributes.
Algorithm 3.3 (Storage Optimization)
Input: Attributes A and an ordered list of functional constraints F of a class C.
1. Compute the sets Astore ⊆ A and Fcomp ⊆ F :
Astore := A; Fcomp := ∅;
for fc ∈ F do
if head(fc) ∈ Astore and Fcomp ∪ {fc} is acyclic then
begin
Astore := Astore \ {head(fc)};
Fcomp := Fcomp ∪ {fc};
end ;

8
2. Acomp := A \ Astore;

Output:3 Astore, Acomp , and Fcomp 

The quality of the optimization result depends on two parameters:


• Cardinality of F : Even apparently redundant inverse constraints can improve
the result.
Let, e.g., F = {A = B, A = C}, then the Algorithm 3.3 yields {B, C} as stored
attributes. Adding the inverse constraints {B = A, C = A}, however, improves
the optimization as then only one attribute will be stored (see Example 3.6).
The generation of inverse constraints may be user-guided or supported by tools
(e.g., symbolic mathematical programs for arithmetic constraints). In the sequel
for the trivial but frequent case A=B we assume that the inverse constraint B=A
is always contained in F .
• Processing ordering of F : Distinct orderings of the functional constraints
F may result in optimizations of various quality.
For instance, consider the functional constraint A = compress(B) and its inverse
B = uncompress(A). There are two possibilities: first selecting A = compress(B)
results in storing B and computing A which is much more storage-extensive than
storing A and computing B by uncompress(A). The latter solution is achieved if
the algorithm first selects B = uncompress(A).
The possibly large search space of orderings can be reduced by heuristic search—
a quite common technique in database optimization. The following proposition
states the time complexity of Algorithm 3.3 and introduces the notion of compu-
tation functions.

Proposition 3.4 (Complexity of Storage Optimization)

1. The worst case time complexity is O(m · n3 ), where m = |F | and n = |A|.

2. For each A ∈ Acomp there exists a unique computation function cA (XA ),


XA ⊆ Astore, composed of FC-functions.

Proof

1. Each functional constraint A = fA (X) yields |X| dependencies A ←d B (B ∈


X). Since |X| ≤ n, there are O(n · m) such facts. The acyclicity test of
3
Both, (OID Astore ) as well as (A XA ) for A = fA (XA ) ∈ Fcomp can be regarded as the
decomposed relational schemes of the relation schema formed by the attributes and the OID of
the objects of a class.

9
+
Fcomp ∪ {fc} is done by computing the transitive closure ←d successively. If
+
←d is represented by an n × n matrix, then adding one A ←d B to the current
closure and computing the resulting transitive dependencies can be done with
time complexity O(n2 ). Moreover, restoring the original transitive closure, in
case of a cycle, can be done by copying and has complexity O(n2 ), too. Thus,
the algorithm has a worst case complexity of O(m · n3).
+
2. Fcomp is by construction acyclic, hence ←d is an irreflexive and finite partial
+
ordering. We perform a Noether induction on ←d : by construction, for each
A ∈ A there is exactly one functional constraint A = fA (YA ) ∈ Fcomp . By
inductive hypothesis for all attributes B ∈ YA ∩ Acomp there exists a unique
computation function cB (XB ), XB ⊆ Astore. Then cA (XA ) of attribute A is
constructed from the FC-function fA and cB (XB ). 

The computation functions will be used to compute the values of computed at-
tributes from the stored ones. They are constructed by the following recursive
algorithm:

Algorithm 3.5 (Computation Functions) Let A ∈ Acomp.


computation function(A)
begin fc := that fc′ ∈ Fcomp where head(fc′ ) = A;
cA := funct(fc);
for B ∈ body(fc) ∩ Acomp do
begin
cB := computation function(B);
< substitute cB for variable B in cA >
end
return cA
end 

Note that the recursion terminates since Fcomp is acyclic. Its worst case com-
plexity is polynomial. We demonstrate the storage optimization performed by
Algorithm 3.3 for two classes of Example 1.1.

Example 3.6 (Triangle √ World continued) First we examine class Right-


Triangle with F = {C = A2 + B2 } and A = {A, B, C}.
Step 1 of Algorithm 3.3 yields Astore = {A, B} and Fcomp = F . Subsequently,
step 2 fixes the computed attributes as A √comp = {C}. The computation function
cC (A, B) for attribute C is calculated as A2 + B2 by Algorithm 3.5.
Next let us have a look at class EquilateralTriangle. Here we have:

A = {A, B, C},
F = {A = B, B = A, B = C, C = B} .

10
When the elements of F are ordered as above, then step 1 of Algorithm 3.3
computes Astore and Fcomp as follows:

0. Astore = {A, B, C}, Fcomp = ∅

1. Astore = {B, C}, Fcomp = {A = B}

2. Astore and Fcomp are not modified since insertion of B = A into Fcomp would
cause a cycle.

3. Astore = {C}, Fcomp = {A = B, B = C }

4. Astore and Fcomp are not modified since insertion of C = B into Fcomp would
cause a cycle.

In step 2 we get Acomp = {A, B}. The computation functions are constructed as
cA (C) = fA (fB (C)) = C and cB (C) = fB(C) = C by Algorithm 3.5. 

Example 3.7 (CAD continued) For the CAD hierarchy of Figure 2, Algo-
rithm 3.3 yields the desired storage optimizations as well.

• EllipseArc: Since F = ∅, all attributes are stored.

• NonRotatedEllipseArc: RotateAngle is computed by cRotateAngle() = 0, the other


ones are stored.

• Ellipse: Attributes StartAngle and DeltaAngle are computed by cStartAngle() = 0


and cDeltaAngle () = 2 ∗ π, resp., the other ones are stored.

• CircleArc: One of the three attributes SemiMajorAxis, SemiMinorAxis, and Ra-


dius is stored, the other two are computed.

• NonRotatedEllipse, Circle: As there are no additional constraints, the set of


computed attributes is the union of the computed attributes of their super-
classes, and the set of stored attributes is the intersection of the stored at-
tributes of their superclasses.

For class Circle, e.g., the attributes Center and Radius may be stored. Assuming
the prerequisites of Remark 2.2 a circle object of the storage-optimized taxonomy
consumes 32 Byte, and is thus as storage-efficient as the C++ based model! 

11
4 Taxonomic Inheritance and Adaptation
In this section we focus on taxonomic inheritance of query and update methods
and on how to adapt update methods in order to enforce integrity w.r.t. the
functional constraints (cp. [STSW93, CFPT94]). It is essential to note that the
following discussions are independent of our storage optimization.

4.1 Inheriting Methods


Query Methods. Query methods (signature and code) can be inherited with-
out any special considerations since class taxonomies imply a set inclusion se-
mantics. Overriding inherited query methods, however, is possible for enhancing
the efficiency of the implementation, but it must not lead to a type restriction of
the method input parameters; i.e., the contravariance rule for method overriding
([KA90]) is applied. Note that inherited method signatures cannot be redefined
in C++ at all, hence C++ trivially obeys the contravariance rule.

Update Methods. Inheritance of update methods needs more thought. In the


sequel we suppose that an update method only modifies attributes of that object
to which it is sent and, for the sake of simplicity, that update methods do not
call other ones.

Definition 4.1 (Update Methods) Let C be a class with attributes A and


functional constraints F . An update method u is defined as follows:
update u(<parameters>) begin <body>; check(F ) end
<body> modifies class attributes and check(F ) checks all functional constraints
F ; if it fails, then u is aborted. 

We assume that updates are performed by update methods only since the system
has to ensure the FCs by integrity checks.4 As a consequence no direct update
access to the attributes is possible.

Inheritance of Update Methods. Let C be a superclass of C ′ with functional


constraints F and F ′, resp., and let u be an update method defined in C. Then
u is inherited by C ′ by rewriting check(F ) to check(F ′).
Since the inherited update method u checks all functional constraints of sub-
class C ′, inconsistent modifications are not committed, i.e., the covariance rule
4
Note that currently the issue of efficient integrity checking is an active topic in database
(see e.g. [GSUW94]).

12
for method overriding ([KA90]) is applied to u. Note that taxonomic modeling
entails a coexistence of both the contravariance rule for query methods and the
covariance rule for update methods, contrary to the broad discussion on this topic
for non-taxonomic modeling approaches (see e.g. [Sha94]).

Example 4.2 (Inheritance of Scale) Let update method Scale of class Triangle
be defined as:
Scale(K: double) begin A := K∗A; B := K∗B; C := K∗C; check() end
This method is, e.g., inherited by class EquilateralTriangle and rewritten to:
Scale(K: double) begin A := K∗A; B := K∗B; C := K∗C; check(A=B,B=C) end
It is a property of equilateral triangles that the integrity check of this method
will never fail. 

4.2 Automatic Adaptation of Update Methods


Unfortunately the simple inheritance mechanism described in the last section
often yields methods aborting for almost all input parameters values.

Example 4.3 (Inheritance of ResizeC) Let update method ResizeC of class


Triangle be defined as:
ResizeC(K: double) begin C := K+C; check() end
Rewriting method ResizeC for class EquilateralTriangle yields:
ResizeC(K: double) begin C := K+C; check(A=B,B=C) end
Obviously, the method will only succeed for K=0. 

This behavior absolutely matches the literal semantics of ResizeC. Modifying


only one side of an equilateral triangle does not make any sense (unless object
migration to superclasses is possible, which is not supported well by most existing
C++ based OODBS). A smarter system, however, will try to adapt this method
canonically in such a way that the constraints remain satisfied after each call.

Definition 4.4 (Adaptable Update Methods) Let u be an update method


of class C with attributes A and functional constraints F , and let Xu be the
attributes modified by u.

1. The set DEP(u) ⊆ A of update-dependent attributes is defined as


+
DEP(u) := {A ∈ A | ∃B ∈ Xu : A ←d B}.
2. An attribute A ∈ DEP(u) is called adaptable, iff there is a FC A = fA (XA )
such that each attribute B ∈ XA is either adaptable or an element of Xu . The
set of adaptable attributes is denoted by ADAPT(u).

13
3. u is called adaptable, iff DEP(u) = ADAPT(u). 

DEP(u) denotes those attributes that must be modified because of the functional
dependencies between them and the update attributes Xu . ADAPT(u) con-
tains those attributes that can actually be adapted by means of the FCs. Both
DEP(u) and ADAPT(u) can be determined in polynomial time. As noted for the
storage optimization, more functional constraints imply the adaptibility of more
attributes. Adaptation functions aA are constructed by the following recursive
algorithm (with polynomial worst case complexity):

Algorithm 4.5 (Adaptation Functions) Let A ∈ ADAPT(u).


adaptation function(A)
begin fc := some fc′ ∈ F where head(fc′) = A
and body(fc′ ) \ X ⊆ ADAPT(u);
aA := funct(fc);
for B ∈ body(fc) \ Xu do
begin
aB := adaption function(B);
< substitute aB for variable B in aA >
end ;
return aA
end 

Let us illustrate Definition 4.4 and Algorithm 4.5 by an example.

Example 4.6 (Adaptibility of ResizeC) Let update method u ≡ ResizeC of


class Triangle be defined as in Example 4.3. Here the computed attribute C shall
be set to value K+C. The set of update attributes consists of Xu = {C}.

• EquilateralTriangle: From F = {A = B, B = A, B = C, C = B} we get DEP(u) =


{A, B} and ADAPT(u) = {A, B}. Thus ResizeC is adaptable in this class.

• RightTriangle: From F = {C = A2 + B2 } we get DEP(u) = {A, B}, but
ADAPT(u) = ∅. Thus ResizeC is not adaptable in this class. 

The non-adaptability of ResizeC in class RightTriangle is very natural, because


resizing the hypothenuse of a right triangle does not uniquely determine the
length of the two other sides. In the sequel we restrict our attention to adaptable
update methods only, since treating non-adaptable methods is a special case of
the view update problem ([GPZ88, LS91]).
Algorithm 4.7 (Automatic Adaptation of Update Method) Let u be
an adaptable update method of class C with attributes A and functional con-
straints F , let DEP(u) be the update-dependent attributes of u, and let aA ,
A ∈ ADAPT(u), be the adaptation function computed by Algorithm 4.5.

14
Then u defined as
update u(<parameters>) begin <body>; check(F ) end
is adapted as follows:
update u(<parameters>)
begin <body>; <A := aA (XA ), A ∈ DEP(u)>; check(F ) end 

The update method is rewritten in such a way that all attributes depending on
the updated attributes are consistently modified, too.

Example 4.8 (Automatic Adaptation of ResizeC) Let update method Re-


sizeC of class Triangle be defined as in Example 4.3. Class EquilateralTriangle
inherits this method. Because it is adaptable (see Example 4.6), it can be rewrit-
ten in the following way:

ResizeC(K: double)
begin C := K+C; /* original body */
B := C; A := C; /* adaptation of dependent attributes */
check(A=B,B=C)
end

The adapted method resizes all three sides of the equilateral triangle. 

Now assume that we make the storage optimization for EquiLateralTriangle as in


Example 3.6; i.e., Astore = {C} and Acomp = {A, B}. Then a smart code analyzer
may detect that the last two lines of ResizeC are redundant and can remove them.

5 Mapping Taxonomies to C++ Based OODBS


In this section we describe how to implement our storage optimization and the
inheritance and adaptation mechanism for methods on top of a commercial C++
based OODBS. In C++ based systems attributes (members) once declared in a
class will always be inherited by all its subclasses. The storage optimization, how-
ever, needs the possibility to exclude attributes from inheritance, if they can be
computed in a subclass. To achieve this, a C++ program with embedded object
definition and manipulation statements (like ODL, OML proposed by [Cat94]) is
pre-processed by the following algorithm. This transformation can be integrated
into the preprocessing steps proposed by ODMG–93 (see Figure 4).

Algorithm 5.1 (Preprocessor Transformation to C++) For each class


C defined in the program with attributes A, functional constraints F , query

15
C++ source program
with embedded ODL/OML

preprocessor

C++ source files


ODBMS C++ header files
meta
data

C++ compiler / linker

database

executable application

Figure 4: Preprocessor steps proposed by ODMG

methods, update methods (that are already adapted according to Section 4), and
create methods do:

1. Astore and Acomp are computed by Algorithm 3.3. For all attributes A ∈ Astore
the computation functions cA are constructed by Algorithm 3.5.

2. Two classes are defined: an abstract class C (without extent) and a leaf class
Cext as a direct subclass of C.

3. For each non-inherited but newly defined attribute A ∈ A two virtual member
functions get A() and set A(x) are declared in class C.

4. For each attribute A ∈ Astore (regardless whether inherited or not), a member


store A is defined in class Cext.

5. For each attribute A ∈ Astore in Cext , get A() is overridden by a read access
to store A, and for each A ∈ Acomp, get A() in Cext is overridden by a call of
computation function cA .

6. For each A ∈ Astore, set A(x) is overridden in Cext by writing value x to store A.

7. Each query method in C is defined as member function of class C. Each


attribute access A in its body is replaced by a function call get A().

16
8. For each update method defined in C a virtual member function is declared
in C.

9. Each update method u (regardless whether inherited or not) is overridden


in class Cext as follows: For each A ∈ A, the declaration of a local variable
A initialized by get A() is inserted in front of the method body. After the
constraint check on the local variables succeeds, set A(A) is called for A ∈
Astore.

10. For each create method defined in class C a constructor is defined in class Cext
as follows: For each A ∈ A, the declaration of a (not initialized) local variable
A is inserted in front of the method body. After the constraint check on the
local variables succeeds, set A(A) is called for A ∈ Astore.

Besides, each call of a create method of a class C in the original program is


replaced by a call of the corresponding constructor of class C ext, and each direct
read access on an attribute A is replaced by a function call get A(). 

We illustrate this mapping for the triangle taxonomy of Example 1.1.

Example 5.2 (Triangle continued) Figure 5 shows the classes, attributes,


and methods defined as a result of the preprocessor transformation.
The functions get A(), get B(), get C(), and set A(x), set B(x), set C(x) declared
in the abstract class Triangle are overridden in each extensional class according
to the result of the storage optimization. The query method circumference is
inherited by each subclass and the update methods ResizeA, ResizeB, ResizeC, and
Scale are overridden in the extensional classes by the corresponding adaptations.
ResizeC (see Example 4.8), for instance, is in class EquilateralTriangle Ext defined
as follows:

ResizeC(K: double)
begin var A: double = get A(),
B: double = get B(),
C: double = get C();
C := K+C; B := C; A := C;
check(A=B,B=C);
set C(C)
end

The VERSANT C++ code ([VER94]) of an implementation of the complete


taxonomy can be found in Appendix A. 

17
Triangle_Ext Triangle
store_A, store_B, store_C virtual get_A(), get_B(), get_C()
get_A(), get_B(), get_C() virtual set_A(x), set_B(x), set_C(x)
set_A(x), set_B(x), set_C(x) Circumference()
ResizeA(K) virtual ResizeA(K)
ResizeB(K) virtual ResizeB(K)
ResizeC(K) virtual ResizeC(K)
Scale(K) virtual Scale(K)

RightTriangle EquilateralTriangle

RightTriangle_Ext EquilateralTriangle_Ext
store_A, store_B store_C
get_A(), get_B(), get_C() get_A(), get_B(), get_C()
set_A(x), set_B(x) set_C(x)
ResizeA(K) ResizeA(K)
ResizeB(K) ResizeB(K)
ResizeC(K) ResizeC(K)
Scale(K) Scale(K)

Figure 5: Space-efficient implementation of the triangle taxonomy in C++

6 Summary and Outlook


We have pointed out that fusing object-oriented databases with object-oriented
programming languages like C++, as it is currently promoted with the ODMG
object model, may cause controversies concerning the approach taken for appli-
cation modeling. Taxonomic modeling on the one side promises a more natu-
ral application semantics, whereas C++ implementation hierarchies aim at low
storage cost. Our preference clearly is the taxonomic approach, which requires
that classes possess both a type and an extension, and moreover need functional
constraints to specify subclasses. Structural inheritance, where semantically jus-
tified, can coexist with this taxonomic modeling view. We have presented a
storage optimization algorithm that relieves the application developer from hav-
ing to decide whether a class attribute has to be stored or computed. We have
shown evidence that in all practical cases the same low storage cost can be ob-
tained as in a C++ approach, hence defeating the storage cost argument behind
implementation hierarchies.
When it comes to software reuse, taxonomic modeling has great advantages, in
particular if inherited update methods can be adapted to guarantee semantically
correct behavior. We have presented a new automatic adaptation algorithm,
which increases the scope of software reusability. Technically it turned out that
taxonomic modeling in a natural way suggests the coexistence of the contravari-
ance rule for query method inheritance and of the covariance rule for update

18
method inheritance.
For rapid availability of taxonomic modeling within a C++-based OODBS we
have shown how to write a preprocessor integrated into the C++-environment
of ODMG. To promote taxonomic modeling on a larger scale, however, requires
the extension of the ODMG object model by functional constraints, and towards
more general integrity constraints beyond. Currently we are also investigating the
extension of OODBS by more powerful taxonomical constraints, even including
probabilistic constraints ([KLKG94]). In summary, we think that taxonomic
modeling has a broad range of applicability. In addition to the CAD domain
covered here we expect great benefits in the domains of CASE and multimedia
databases, where complex and large class attributes demand a natural semantic
modeling in combination with a good storage optimization.
Acknowledgment: We like to thank Felix Dutkowski, civil engineer from Nemetschek
Programmsystem GmbH, for providing us with insights into the mentality of C++
programmers as part of the OCAD project.5 Likewise we thank Jutta Seidel, chief
architect from U.T.M. Ingenieurgesellschaft für Hochbau GmbH, Regensburg,
for insights into architectural CAD applications. Furthermore, we thank Hans
Kempe and Robert Hitzelberger from FORWISS for fruitful discussions and Ver-
sant support.

5
“OCAD: Object-Oriented Databases and CAD” is a joint project of Nemetschek Programm-
system GmbH, Munich (a large CAD tool manufacturer in Europe) and the Bavarian Research
Center for Knowledge-Based Systems (FORWISS) under the direction of W. Kießling.

19
References
[AB91] S. Abiteboul and A. Bonner. Objects and views. In Proc. ACM SIGMOD
Conf., pages 238–247, Denver, CO, May 1991.

[ABD+ 89] M. Atkinson, F. Bancilhon, D. DeWitt, K. Dittrich, D. Maier, and


S. Zdonik. The object-oriented database system manifesto. In Proceedings
of the First International Conference on Deductive and Object-Oriented
Databases, pages 40–57, 1989.

[BCV91] A.P. Buchmann, R.S. Carrera, and M.A. Vazquez-Galindo. Handling con-
straints and their exceptions: An attached constraint handler for object-
oriented CAD databases. In Dittrich, Dayal, and Buchmann, editors,
Object-Oriented Database Systems, chapter II 6., pages 65–86. Springer,
Berlin, 1991.

[Boo94] G. Booch. Object-Oriented Analysis and Design—with Applications. Ben-


jamin/Cummings, 2nd edition, 1994.

[Cat94] R.G.G. Cattel, editor. The Object Database Standard: ODMG–93. Morgan
Kaufmann Publishers, San Mateo, California, 1994.

[CFPT94] S. Ceri, P. Fraternali, S. Paraboschi, and L. Tanca. Automatic generation


of production rules for integrity maintenance. ACM Trans. on Database
Systems, 13(3):367–422, Sept. 1994.

[CY91] P. Coad and E. Yourdon. Object-Oriented Analysis. Yourdon Press Com-


puting Series. Prentice Hall, Englewood Cliffs, 2nd edition, 1991.

[GPZ88] G. Gottlob, P. Paolini, and R. Zicari. Properties and update semantics of


consistent views. ACM Trans. on Database Systems, 13(4):486–524, 1988.

[GSUW94] A. Gupta, Y. Sagiv, J.D. Ullman, and J. Widom. Constraint checking with
partial information. In Proc. ACM SIGACT-SIGMOD-SIGART Symp. on
Principles of Database Systems., pages 45–55, Minneapolis, 1994.

[KA90] S. Khoshafian and R. Abnous. Object Orientation—Concepts, Languages,


Databases, User Interfaces. Wiley, New York, 1990.

[Kim90] W. Kim. Introduction to Object-Oriented Databases. The MIT Press, Cam-


bridge, Massachusetts, 1990.

[KLKG94] W. Kießling, Th. Lukasiewicz, G. Köstler, and U. Güntzer. The


TOP database model—taxonomy, object-orientation and probability. In
Laks V.S. Lakshmanan, editor, Proc. Post-ILPS’94 Workshop on Un-
certainty in Database and Deductive Systems, pages 71–82, Ithaca, NY,
November 1994.

[LP91] W. LaLonde and J. Pugh. Subclassing 6= subtyping 6= is-a. J. Object-


Oriented Programming, 3(5):57–62, Jan. 1991.

20
[LS91] J.A. Larson and A.S. Sheth. Updating relational views using knowledge at
view definition and view update time. Inf. Sys., 16(2):145–168, 1991.

[MP92] D.E. Monarchi and G.I. Puhr. A research typology for object-oriented
analysis and design. Communications of the ACM, 35(9):35–47, September
1992.

[OSt94] ObjectStore technical overview, release 3.0. Object Design Inc., One New
England Executive Park, Burlington, 01803, 1994.

[RBP+ 91] J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, and W. Lorensen.


Object-Oriented Modeling and Design. Prentice-Hall, 1991.

[Sha94] David L. Shang. Covariant specifications. ACM SIGPLAN Notices,


29(12):58–65, Dec. 1994.

[Str91] Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley,


Reading,MA, 2nd edition, 1991.

[STSW93] K.-D. Schewe, B. Thalheim, J. W. Schmidt, and I. Wetzel. Integrity en-


forcement in object-oriented databases. In Modelling Database Dynamics,
pages 174–195. Springer, 1993.

[Ull88] Jeffrey D. Ullman. Principles of Database and Knowledge–Base Systems,


volume 1. Computer Science Press, 1988.

[VER94] System manual, release 3.0. Versant Object Technology Corporation, Park,
CA 94025, 1994.

[Wil91] M.J. Willshire. How spacey can they get? Space overhead for storage and
indexing with object-oriented databases. In Proc. IEEE Conf. on Data
Eng., pages 14–22, Kobe, Japan, Apr. 1991.

[ZM90] Z.B. Zdonik and D. Maier. Fundamentals of object-oriented databases. In


Z.B. Zdonik and D. Maier, editors, Readings in Object-Oriented Database
Systems. Morgan Kaufmann Publishers, 1990.

21
Appendix A

A.1 The Triangle Taxonomy


The triangle taxonomy of Figure 1 is stated in an extended C++ notation.
Note that the access specifiers used here (attributes, create, etc.) differ from
standard C++ access specifiers (public, protected, private). By using these
new specifiers the preprocessor does not need to inspect code in order to decide
whether an attribute, a create method, an update method, or a query method
is defined. Moreover, a further specifier constraints is used to state functional
constraints. By defining class Triangle as subclass of class PObject, it is stated
that class Triangle is persistency capable ([Cat94]), i.e., that Triangle is capable
of having persistent as well as transient instances.

// Triangle.h

//===============================================================
// Standard includes
//===============================================================

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <iostream.h>

//===============================================================
// Triangle Taxonomy
//===============================================================

//---------------------------------------------------------------
// class Triangle
//---------------------------------------------------------------

class Triangle : public PObject // PObject = top class of


{ // persitent objects
attributes:
double A; double B; double C;

create:
Triangle(double a, double b, double c) {A=a; B=b; C=c};

query:
double circumference();
virtual void print();

22
update:
void ResizeA(double K);
void ResizeB(double K);
void ResizeC(double K);
};

//---------------------------------------------------------------
// class RightTriangle
//---------------------------------------------------------------

class RightTriangle : public Triangle


{
create:
RightTriangle(double a, double b) {A=a; B=b; C=sqrt(a*a+b*b);};

query:
void print(); // override print

constraints:
C=sqrt(A*A+B*B)
};

//---------------------------------------------------------------
// class EquilateralTriangle
//---------------------------------------------------------------

class EquilateralTriangle : public Triangle


{
create:
EquilateralTriangle(double a) {A=a; B=a; C=a;};

query:
void print(); // override print

constraints:
A=B, B=C
};

23
// Triangle.C

#include "Triangle.h"

//===============================================================
// Definition of query methods
//===============================================================

double Triangle::circumference()
{return A + B + C;};

void Triangle::print()
{cout <<"Triangle: "<<A<<" "<<B<<" "<<C<<"\n";}
void RightTriangle::print()
{cout <<"RightTriangle: "<<A<<" "<<B<<" "<<C<<"\n";}
void EquilateralTriangle::print()
{cout <<"EquilateralTriangle: "<<A<<" "<<B<<" "<<C<<"\n";}

//===============================================================
// Definition of update methods
//===============================================================

void Triangle::ResizeA(double K) {A = A + K;};


void Triangle::ResizeB(double K) {B = B + K;};
void Triangle::ResizeC(double K) {C = C + K;};

24
A.2 Versant C++ Code of the Triangle Taxonomy
The triangle taxonomy is transformed into Versant C++ code ([VER94]) by
means of Algorithm 5.1. For the sake of simplicity, update method aborting is
realized by doing nothing but printing a warning message.

// Triangle.transform.h

#ifndef TRIANGLE // this test is needed by Versant


#define TRIANGLE

//===============================================================
// Versant includes
//===============================================================

#include <cxxcls/pobject.h>
#include <cxxcls/vstring.h>
#include <cxxcls/vlist.h>

//===============================================================
// Standard includes
//===============================================================

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <iostream.h>

//===============================================================
// Triangle Taxonomy
//===============================================================

//---------------------------------------------------------------
// class Triangle
//---------------------------------------------------------------

class Triangle : public PObject // PObject = Verant top class


{ // of persistent objects
public:
virtual ~Triangle(); // needed by Versant

virtual double get_A() = 0;


virtual double get_B() = 0;
virtual double get_C() = 0;

25
virtual void set_A(double X) = 0;
virtual void set_B(double X) = 0;
virtual void set_C(double X) = 0;

double circumference();
virtual void print() = 0;

virtual void ResizeA(double K) = 0;


virtual void ResizeB(double K) = 0;
virtual void ResizeC(double K) = 0;
};

class Triangle_Ext : public Triangle


{
private:
double store_A;
double store_B;
double store_C;

public:
virtual ~Triangle_Ext(); // needed by Versant
Triangle_Ext(double a, double b, double c)
{double A; double B; double C;
A=a; B=b; C=c;
set_A(A); set_B(B); set_C(C);};

double get_A() {return store_A;};


double get_B() {return store_B;};
double get_C() {return store_C;};

void set_A(double X) {dirty(); store_A = X;};


void set_B(double X) {dirty(); store_B = X;};
void set_C(double X) {dirty(); store_C = X;};
// dirty() is a versant specific function
// that has to be called when an object is modified

void print();

void ResizeA(double K);


void ResizeB(double K);
void ResizeC(double K);
};

26
//---------------------------------------------------------------
// class RightTriangle
//---------------------------------------------------------------

class RightTriangle : public Triangle


{
public:
virtual ~RightTriangle(); // needed by Versant
};

class RightTriangle_Ext : public RightTriangle


{
private:
double store_A;
double store_B;

public:
virtual ~RightTriangle_Ext(); // needed by Versant
RightTriangle_Ext(double a, double b)
{double A; double B; double C;
A=a; B=b; C=sqrt(a*a+b*b);
if (C=sqrt(A*A+B*B))
{set_A(A); set_B(B);}
else
{cout << "RightTriangle Warning: create failed\n";};
};

double get_A() {return store_A;};


double get_B() {return store_B;};
double get_C() {return sqrt(get_A()*get_A()+get_B()*get_B());};

void set_A(double X) {dirty(); store_A = X;};


void set_B(double X) {dirty(); store_B = X;};
void set_C(double X) {}; // dummy method

void print();

void ResizeA(double K);


void ResizeB(double K);
void ResizeC(double K);
};

27
//---------------------------------------------------------------
// class EquilateralTriangle
//---------------------------------------------------------------

class EquilateralTriangle : public Triangle


{
public:
virtual ~EquilateralTriangle(); // needed by Versant
};

class EquilateralTriangle_Ext : public EquilateralTriangle


{
private:
double store_C;

public:
virtual ~EquilateralTriangle_Ext(); // needed by Versant
EquilateralTriangle_Ext(double a)
{double A; double B; double C;
A=a; B=a; C=a;
if ((A=B) && (B=C))
{set_C(C);}
else
{cout << "EquilateralTriangle Warning: create failed\n";};
};

double get_A() {return get_C();};


double get_B() {return get_C();};
double get_C() {return store_C;};

void set_A(double X) {}; // dummy method


void set_B(double X) {}; // dummy method
void set_C(double X) {dirty(); store_C = X;};

void print();

void ResizeA(double K);


void ResizeB(double K);
void ResizeC(double K);
};

#endif

28
// Triangle.transform.C

#include "Triangle.transform.h"

//===============================================================
// Versant specific definitions
//===============================================================

Implement PClassObj<Triangle>;
Implement PClassObj<RightTriangle>;
Implement PClassObj<EquilateralTriangle>;

Implement PClassObj<Triangle_Ext>;
Implement PClassObj<RightTriangle_Ext>;
Implement PClassObj<EquilateralTriangle_Ext>;

Triangle::~Triangle(){};
Triangle_Ext::~Triangle_Ext(){};

RightTriangle::~RightTriangle(){};
RightTriangle_Ext::~RightTriangle_Ext(){};

EquilateralTriangle::~EquilateralTriangle(){};
EquilateralTriangle_Ext::~EquilateralTriangle_Ext(){};

//===============================================================
// Definition of query methods
//===============================================================

double Triangle::circumference()
{return get_A() + get_B() + get_C();};

void Triangle_Ext::print()
{cout <<"Triangle: "
<<get_A()<<" "<<get_B()<<" "<<get_C()<<"\n";}
void RightTriangle_Ext::print()
{cout <<"RightTriangle: "
<<get_A()<<" "<<get_B()<<" "<<get_C()<<"\n";}
void EquilateralTriangle_Ext::print()
{cout <<"EquilateralTriangle: "
<<get_A()<<" "<<get_B()<<" "<<get_C()<<"\n";}

29
//===============================================================
// Definition of adapted update methods
//===============================================================

void Triangle_Ext::ResizeA(double K)
{double A = get_A(); A = A + K; set_A(A);};
void Triangle_Ext::ResizeB(double K)
{double B = get_B(); B = B + K; set_B(B);};
void Triangle_Ext::ResizeC(double K)
{double C = get_C(); C = C + K; set_C(C);};

void RightTriangle_Ext::ResizeA(double K)
{cout << "RightTriangle Warning: ResizeA not adaptable\n";};
void RightTriangle_Ext::ResizeB(double K)
{cout << "RightTriangle Warning: ResizeB not adaptable\n";};
void RightTriangle_Ext::ResizeC(double K)
{cout << "RightTriangle Warning: ResizeC not adaptable\n";};

void EquilateralTriangle_Ext::ResizeA(double K)
{double A = get_A(); double B = get_B(); double C = get_C();
A = A+K; B = A; C = A;
if ((A=B) && (B=C))
{set_C(C);}
else
{cout << "EquilateralTriangle Warning: ResizeA failed\n";};
};
void EquilateralTriangle_Ext::ResizeB(double K)
{double A = get_A(); double B = get_B(); double C = get_C();
B = B+K; A = B; C = B;
if ((A=B) && (B=C))
{set_C(C);}
else
{cout << "EquilateralTriangle Warning: ResizeB failed\n";};
};
void EquilateralTriangle_Ext::ResizeC(double K)
{double A = get_A(); double B = get_B(); double C = get_C();
C = C+K; A = C; B = C;
if ((A=B) && (B=C))
{set_C(C);}
else
{cout << "EquilateralTriangle Warning: ResizeC failed\n";};
};

30
A.3 Two Simple Applications
Let us give two simple applications of the triangle taxonomy. The first one inserts
three new triangle objects into the database and prints the database content.

// InsertTriangle.C
// insert three new triangle objects into the class

#include "Triangle.h"
#define DBNAME "TriangleDB" //database used
#define SESSION "TriDB"

main()
{
::dom = new PDOM;
::dom->beginsession(DBNAME,SESSION);

// create three triangles


Triangle*
t = new Persistent Triangle(5,6,7);
t = new Persistent RightTriangle(33,44);
t = new Persistent EquilateralTriangle(1000);

// select all triangles in the database


LinkVstr<Triangle> triangle_list =
PClassObject<Triangle>::Object().select(
NULL, // use default DB
TRUE, // search subclasses, too
NULL_PREDICATE // all instances
);

// print each triangle


cout << "\n---------------------\n";
cout << "Triangles:\n";
for (int i = 0; i < triangle_list.size(); i++)
{
triangle_list[i]->print();
};
cout << "---------------------\n\n";

::dom->commit();
::dom->exit(0);
}

31
The second application calls ResizeC(10) for each triangle object in the database
and prints the updated database content.

// UpdateTriangle.C
// call ResizeC(10) for each triangle in the database

#include "Triangle.h"

#define DBNAME "TriangleDB" // database used


#define SESSION "TriDB"

main()
{
::dom = new PDOM;
::dom->beginsession(DBNAME,SESSION);

// select all triangles in the database


LinkVstr<Triangle> triangle_list =
PClassObject<Triangle>::Object().select(
NULL, // use default DB
TRUE, // search subclasses, too
NULL_PREDICATE // all instances
);

// resize each triangle and print it


cout << "\n---------------------\n";
cout << "Triangles:\n";
for (int i = 0; i < triangle_list.size(); i++)
{
triangle_list[i]->ResizeC(10);
triangle_list[i]->print();
};
cout << "---------------------\n\n";

::dom->commit();
::dom->exit(0);
}

32
A.4 Versant C++ Code of the Applications
The applications are transformed as well. The modifications are, however, very
simple. Thus, the user can easily interpret compiler error messages of the trans-
formed code.

// InsertTriangle.transform.C
// insert three new triangle objects into the class

#include "Triangle.transform.h" // <-- modified


#define DBNAME "TriangleDB" //database used
#define SESSION "TriDB"

main()
{
::dom = new PDOM;
::dom->beginsession(DBNAME,SESSION);

// create three triangles


Triangle*
t = new Persistent Triangle_Ext(5,6,7); // <-- modified
t = new Persistent RightTriangle_Ext(33,44); // <-- modified
t = new Persistent EquilateralTriangle_Ext(1000); // <-- modified

// select all triangles in the database


LinkVstr<Triangle> triangle_list =
PClassObject<Triangle>::Object().select(
NULL, // use default DB
TRUE, // search subclasses, too
NULL_PREDICATE // all instances
);

// print each triangle


cout << "\n---------------------\n";
cout << "Triangles:\n";
for (int i = 0; i < triangle_list.size(); i++)
{
triangle_list[i]->print();
};
cout << "---------------------\n\n";

::dom->commit();
::dom->exit(0);
}

33
// UpdateTriangle.transform.C
// call ResizeC(10) for each triangle in the database

#include "Triangle.transform.h" // <-- modified

#define DBNAME "TriangleDB" // database used


#define SESSION "TriDB"

main()
{
::dom = new PDOM;
::dom->beginsession(DBNAME,SESSION);

// select all triangles in the database


LinkVstr<Triangle> triangle_list =
PClassObject<Triangle>::Object().select(
NULL, // use default DB
TRUE, // search subclasses, too
NULL_PREDICATE // all instances
);

// resize each triangle and print it


cout << "\n---------------------\n";
cout << "Triangles:\n";
for (int i = 0; i < triangle_list.size(); i++)
{
triangle_list[i]->ResizeC(10);
triangle_list[i]->print();
};
cout << "---------------------\n\n";

::dom->commit();
::dom->exit(0);
}

34

You might also like