The information about metaobject protocol (MOP) is organized in the following manner:
There are several classes in the metaobject protocol whose instances represent schema objects other than types, such as os_base_class and os_member and its subtypes. These auxiliary classes are part of the metaobject protocol (MOP) but are not metatypes and are not, therefore, part of the metatype hierarchy. Descriptions of these auxiliary classes begin on page 206.
Besides telling you how to perform run-time read access to ObjectStore application, compilation, and database schemas, this chapter explains how to create classes dynamically and add them to ObjectStore database schemas.
MOP Header Files
Programs using the metaobject protocol must include <ostore/ostore.hh>, followed by <ostore/coll.hh> (if collection is being used), followed by <ostore/mop.hh>. Attributes of MOP Classes
It is useful to think of classes in the metaobject protocol as having attributes, that is, pieces of abstract state that you can access using create, get, and set functions. Attributes of
Consider for example the class os_class_type, whose instances represent C++ classes. Here is a diagram showing its attributes.
os_class_type
Key to arrow shades
create() function for MOP classes
The create function (or functions) for each class is called create(), and is used to create instances of the class. The get and set functions for each attribute have names based on the attribute name, as follows. For attributes that are not Boolean valued, the functions are
get_ attribute-name()and
set_ attribute-name()For Boolean-valued attributes, the get and set functions are, respectively,
attribute-name()and
set_ attribute-name()
Creating a transient schema
Creating classes in the transient schema always starts in one of two ways:
In the MOP interface, pointer arguments to create and set functions generally indicate that 0 is an acceptable initial value for the argument's corresponding attribute; if 0 is not an acceptable value, a reference (&) argument is used instead of a pointer argument. But note that a nonnull value for the attribute might have to be supplied before installation or evolution, in order to meet the consistency requirements.
For an attribute that must have a nonnull value in order to meet the consistency requirements, the get functions return a reference type (unless they return a built-in type). When performed on an object that does not yet have a nonnull value for the attribute, such a get function returns an unspecified object (see The is_unspecified() function).
For an attribute that might have a null value and still meet the consistency requirements, the get functions return a pointer type (unless they return a built-in type).
Retrieving an Object Representing the Type of a Given Object
The type_at() Function
You can retrieve an instance of os_type that represents the type of a given persistent object by passing the object's address to the static member function os_type::type_at(). This function is declared as follows: type_at() declaration
static const os_type *type_at(const void *p) ;Note that p must point to persistent memory.
For pointers that point to the beginning of more than one object (that is, pointers that point to co-located objects), this function returns the type of the outermost object. For example, consider a pointer typed as a part* that points to a direct instance of mechanical_part, derived from part; and suppose that part has no base types. Passing this pointer to type_at() results in an os_type representing the class mechanical_part - unless the object is embedded as a data-member value in the initial bytes of some other object, in which case the function would return an os_type representing this other object.
f () { part *p = ... ; . . . const os_type *t = os_type::type_at(p); . . . }
static const os_type *type_containing( const void *p, const void*& p_container, os_unsigned_int32& element_count );Unlike type_at(), the object whose type is returned does not necessarily begin at the specified address; that is, the argument p might point to a subobject or data-member value embedded in the middle of the object whose type is returned.
The address of the object whose type is returned, the outermost object containing the specified object, is referred to by p_container when the function returns.
Arrays are handled specially by type_containing(). If the outermost containing object is an array, the element type of the array is returned, and element_count is set to refer to the array's size. If the containing object is not an array, element_count is set to 1.
Instances of these classes represent ObjectStore schemas.
static const os_app_schema &os_app_schema::get( const database&); static const os_comp_schema &os_comp_schema::get( const database&); static const os_database_schema &os_database_schema::get( const database&);
static const os_app_schema &os_app_schema::get();
os_Collection<const os_class_type*>You can also retrieve the class with a given name in a given schema with os_schema::find_type().
os_schema::get_classes() const;
const os_type *os_schema::find_type(const char*) const;
f () { os_database *my_db = os_database::open("/foo/bar/comp_schema"); os_Collection<const os_class_type*> the_classes = os_comp_schema::get(*my_db).get_classes(); . . . }You can retrieve the os_class_types in a given application schema this way:
f () { os_database *my_db = os_database::open("/foo/bar/app_schema"); os_Collection<const os_class_type*> the_classes = os_app_schema::get(*my_db).get_classes(); . . . }You can retrieve the types in a database schema this way:
f () { os_database *my_db = os_database::open("/foo/bar/db1"); os_Collection<const os_class_type*> the_classes = os_database_schema::get(*my_db).get_classes(); . . . }Finally, you can retrieve the class with a given name in a given schema this way:
f () { os_database *my_db = os_database::open("/foo/bar/db1"); const os_type *the_type = os_database_schema::get(*my_db).find_type("part"); . . . }See the entries for the classes os_schema, os_comp_schema, os_app_schema, and os_database_schema in the ObjectStore C++ API Reference.
static void initialize() ;Recall that creating classes in the transient schema always starts in one of two ways:
static void copy_classes( const os_schema &schema, os_Set<const os_class_type*> &classes ) ;The first argument is the schema containing the classes to be copied, and the second argument is a set of pointers to the classes to be copied. So before calling this function, you must perform these steps:
const os_app_schema &the_app_schema = os_app_schema::get() ;
os_Set<const os_class_type*> to_be_copied_to_transient_schema ;
const os_type *the_const_type_os_collection = the_app_schema.find_type("os_collection") ;
os_mop::copy_classes( the_app_schema, to_be_copied_to_transient_schema ) ;
static os_type *find_type(const char *name) ;Notice that os_mop::find_type(), unlike os_schema::find_type(), returns a pointer to a non-const os_type. This means you can modify the os_type by performing set functions on it, and you can retrieve other modifiable objects by performing get functions on it. You can also make other objects in the transient schema refer to it.
For example, if you do
os_type *the_non_const_type_os_collection = os_mop::find_type("os_collection") ;You can then create a collection-valued data member (see The Class os_member_variable):
assert (the_non_const_type_os_collection) ; os_member_variable &new_member = os_member_variable::create( member_name, the_non_const_type_os_collection ) ;
void install(os_schema &new_schema) ;
static os_schema &get_transient_schema() ;
static os_database_schema &get_for_update(
const os_database&)
os_database_schema::get_for_update(*db).install( os_mop::get_transient_schema() ) ;
void install (os_schema &new_schema, os_schema_install_options ) ;See os_schema_install_options in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a description of the specific installation options available.
The os_schema_install_options allows you to control whether member functions are copied into the database schema during installation. The default behavior is to not copy member functions.
os_schema_evolution::evolve()
If you want to modify a database schema in a way that requires evolution, you should use os_schema_evolution::evolve() rather than install().
static void evolve( const char *workdb_name, const char*database_name, os_schema &new_schema ) ;The new_schema argument should be the transient schema. So a call to evolve might look like
static void evolve( "/example/workdb", "/example/partsdb", os_mop::get_transient_schema() ) ;
Types with const or volatile specifiers are represented by instances of os_anonymous_indirect_type, and typedefs are represented by instances of os_named_indirect_type.
All the classes in the metatype hierarchy are documented in Chapter 2, Class Library, of the ObjectStore C++ API Reference. Note that these are not all the types in the metaobject protocol. There are several classes in the metaobject protocol whose instances represent schema objects other than types, such as os_base_class (see The Class os_base_class), and os_member and its subtypes (see The Class os_member). The rest of this chapter contains a section on each class in the protocol.
The Class os_type
Below is a diagram showing the attributes of the class os_type. Each arrow represents an attribute and points to its value type. The darkest arrows have corresponding set functions, get functions, and create arguments. The medium arrows have corresponding set functions and get functions. The lightest arrows have corresponding get functions only.
Attributes of os_type
Create Functions
This class defines no create functions. You always create an instance of os_type using a create function defined by one of the subtypes of os_type. The kind Attribute
The kind of an os_type is an enumerator indicating what kind of type is represented by the os_type. The enumerators are kind enumerators
os_type::Void os_type::Named_indirect os_type::Anonymous_indirect os_type::Char os_type::Unsigned_char os_type::Signed_char os_type::Unsigned_short os_type::Signed_short os_type::Integer os_type::Unsigned_integer os_type::Signed_long os_type::Unsigned_long os_type::Float os_type::Double os_type::Long_double os_type::Pointer os_type::Reference os_type::Pointer_to_member os_type::Array os_type::Class os_type::Instantiated_class os_type::Enum os_type::Function os_type::Type
os_type_kind get_kind() const ;Given an object typed as an os_type, you can use get_kind() to determine the subtype of os_type that the object is an instance of. Then you can convert the object to that subtype using the type-safe conversion operators. See Type-Safe Conversion Operators.
Retrieving the kind_string Attribute
There is a static member function that returns the name of a given os_type_kind:
static const char *get_kind_string(os_type_kind) ;For example os_type::get_kind_string(os_type::Class) returns Class.
char *get_string() const ;Note that this function allocates the returned string on the heap, so you should delete it when it is no longer needed.
os_boolean is_const() const ;
os_boolean is_volatile() const ;
os_boolean is_integral_type() const ;
os_boolean is_real_type() const ;
There is a pair of get functions, get_enclosing_class(), one taking const this and returning a const os_class_type*, and one taking non-const this and returning an os_class_type*.
const os_class_type *get_enclosing_class() const ; os_class_type *get_enclosing_class() ;The function returns 0 if there is no enclosing class.
const os_type &strip_indirect_types() const ; os_type &strip_indirect_types() ;For types with const or volatile specifiers, this function returns the type being specified as const or volatile. For example, if the specified os_type represents the type const int, strip_indirect_types() will return an os_type representing the type int. If the specified os_type represents the type char const * const, strip_indirect_types() will return an os_type representing the type char const *.
For typedefs, this function returns the original type for which the typedef is an alias.
This function calls itself recursively, until the result is not an os_indirect_type. So, for example, consider an os_named_indirect_type representing
typedef const part const_partThe result of applying strip_indirect_types() to this is an os_class_type representing the class part (not an os_anonymous_indirect_type representing const part - which would be the result of os_indirect_type::get_target_type()).
In the transient schema, if such an attribute lacks a value (because you have not yet specified it), the get function returns the unspecified type. This is the only os_type for which the following predicate returns nonzero:
os_boolean is_unspecified() const ;
operator os_void_type&() ; operator os_named_indirect_type&() ; operator os_anonymous_indirect_type&() ; operator os_integral_type&() ; operator os_real_type&() ; operator os_pointer_type&() ; operator os_reference_type&() ; operator os_pointer_to_member_type&() ; operator os_array_type&() ; operator os_class_type&() ; operator os_instantiated_class_type&() ; operator os_enum_type&() ; operator os_function_type&() ;The existence of these operators allows you to supply an expression of type os_type or os_type& as the actual parameter for a formal parameter of type, for example, os_integral_type& - provided the designated os_type is actually an instance of os_integral_type. If it is not, err_mop_illegal_cast is signaled. Each of these operators is type safe in the sense that err_mop_illegal_cast is always signaled if it is used to perform an inappropriate conversion.
There are also conversion operators for converting a const os_type& to a const reference to any of the subtypes of os_type:
operator const os_void_type&() const ; operator const os_named_indirect_type&() const ; operator const os_anonymous_indirect_type&() const ; operator const os_integral_type&() const ; operator const os_real_type&() const ; operator const os_pointer_type&() const ; operator const os_reference_type&() const ; operator const os_pointer_to_member_type&() const ; operator const os_array_type&() const ; operator const os_class_type&() const ; operator const os_instantiated_class_type&() const ; operator const os_enum_type&() const ; operator const os_function_type&() const ;
int unsigned int short unsigned short long unsigned long char signed char unsigned charSee os_integral_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Attribute of
This class defines one attribute, is_signed:
os_integral_type
Create Functions
This class has several create functions for the various kinds of integer types.
static os_integral_type &create_signed_char() ; static os_integral_type &create_unsigned_char() ; static os_integral_type &create_defaulted_char( os_boolean signed) ; static os_integral_type &create_short(os_boolean signed) ; static os_integral_type &create_int(os_boolean signed) ; static os_integral_type &create_long(os_boolean signed) ;
os_boolean is_signed() const ;
float double long doubleSee os_real_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Functions
This class has three create functions, one for each of the real types listed above.
static os_real_type &create_float() ; static os_real_type &create_double() ; static os_real_type &create_long_double() ;
See os_class_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Attributes of
Below is a diagram showing some of the important pieces of abstract state associated with the class os_class_type. Each arrow represents a piece of state and points to its value type. In the case of double arrows, the value type is an os_List, and the type pointed to by the arrow is the element type of the os_List.
os_class_type
Create Functions
This class has two create functions:
static os_class_type &create ( const char *name ) ;The argument specifies the initial value for the name attribute. The initial values for the remaining attributes are as follows:
The second overloading allows specification of more attribute initial values:
static os_class_type &create ( const char *name , const os_List<os_base_class*> &base_classes , const os_List<os_member*> &members , os_boolean defines_virtual_functions ) ;The arguments specify the initial values for the attributes name, base_classes, members, and defines_virtual_functions. The initial values for the remaining attributes are as follows:
Attribute | Value |
---|---|
class_kind | os_class_type::Class |
defines_get_os_typespec_function | 0 |
is_template_class | 0 |
is_persistent | 0 |
is_forward_definition | 0 |
The name Attribute
Getting the attribute
The name of the class represented by a given instance of os_class_type is returned by the following function:
const char *get_name() const ;The returned value points to the beginning of the persistent character array holding the class's name.
void set_name(const char*) ;
os_unsigned_int32 get_class_kind() const ;
void set_class_kind(os_unsigned_int32) ;
Getting the attribute
You can retrieve a list of the members of a specified class using the following functions:
os_List<os_member*> get_members() ; os_List<const os_member*> get_members() const ;These functions signal err_mop_forward_definition if the value of is_forward_definition is nonzero for the specified os_class_type.
void set_members(const os_List<os_member*>&) ;This replaces the members of the specified class with the specified members.
You can also initialize the members of a class with a create function; see Create Functions.
os_base_class Objects
As with members, the os_base_class objects associated with a class are sometimes manipulated as a group. The list has one of the following two types:
Retrieving the objects
You can retrieve a list of the os_base_class objects for a specified class using the following functions:
os_List<os_base_class*> get_base_classes() ; os_List<const os_base_class*> get_base_classes() const ;These functions signal err_mop_forward_definition if the value of is_forward_definition is nonzero for the specified os_class_type.
void set_base_classes(const os_List<os_base_class*>&) ;This replaces the os_base_class objects for the specified class with the specified os_base_class objects.
The declares_get_os_typespec_function Function
You can determine if a given class declares a get_os_typespec() member function with
os_boolean declares_get_os_typespec_function() const ;which returns nonzero if the class does declare such a member function, and 0 otherwise.
void set_declares_get_os_typespec_function(os_boolean) ;If you supply 1, the class will declare such a member function; if you supply 0, it will not.
When you attempt to install a class in a schema, if a function in members is virtual, defines_virtual_functions must be nonzero, or installation will fail.
os_boolean defines_virtual_functions() const ;
void set_defines_virtual_functions(os_boolean) ;
The introduces_virtual_functions Attribute
The value of this attribute for a given class is nonzero if the class defines a virtual function but does not inherit any virtual functions. Getting the attribute
You can retrieve this value with the following function:
os_boolean introduces_virtual_functions() const ;
void set_introduces_virtual_functions(os_boolean) ;
os_boolean is_forward_definition() const ;
void set_is_forward_definition(os_boolean) ;
os_boolean is_persistent() const ;
void is_persistent(os_boolean) ;
const os_base_class *find_base_class(const char *name) const ; os_base_class *find_base_class(const char *name) ;The functions return 0 if there is no such base class, and signal err_forward_definition if this is a forward definition.
os_List<const os_base_class*> get_allocated_virtual_base_classes() const; os_List<os_base_class*> get_allocated_virtual_base_classes() ;The function returns 0 if there are no such base classes, and signals err_forward_definition if this is a forward definition.
os_List<const os_base_class*> get_indirect_virtual_base_classes() const; os_List<os_base_class*> get_indirect_virtual_base_classes() ;The function returns 0 if there are no such base classes, and signals err_forward_definition if this is a forward definition.
const os_member_variableThe function returns 0 if there is no such member.
*find_member(const char *name) const ; os_member_variable *find_member(const char *name) ;
static const os_class_type &get_most_derived_class ( const void *object, const void* &most_derived_object ) const ;If object points to the value of a data member for some other object, o, this function returns a reference to the most derived class of which o is an instance. A class, c1, is more derived than another class, c2, if c1 is derived from c2, or derived from a class derived from c2, and so on. most_derived_object is set to the beginning of the instance of the most derived class. There is one exception to this behavior, described below.
If object points to an instance of a class, o, but not to one of its data members (for example, because the memory occupied by the instance begins with a virtual table pointer rather than a data member value), the function returns a reference to the most derived class of which o is an instance. most_derived_object is set to the beginning of the instance of the most derived class. There is one exception to this behavior, described below.
If object does not point to the memory occupied by an instance of a class, most_derived_object is set to 0, and err_mop is signaled. ObjectStore issues an error message like the following:
<err-0008-0010>Unable to get the most derived class in os_class_type::get_most_derived_class() containing the address 0x[[some-address]].
Class and
class B { public: int ib ; } ; class D : public B { public: int id ; } ; class C { public: int ic ; D cm ; } ; void baz () { C* pC = new (db) C; D *pD = &pC->cm ; int *pic = &pC->ic, *pid = &pC->cm.id, *pib = &pC->cm.ib ; . . . } Function resultInvoking get_most_derived_class() on the pointers pic, pid, and pib has the results shown in the following table:
object | most_derived_object | os_class_type |
---|---|---|
pic | pC | C |
pid | pD | D |
pib | pD | D |
The exception to the behavior described above can occur when a class-valued data member is collocated with a base class of the class that defines the data member. If a pointer to such a data member (which is also a pointer to such a base class) is passed to get_most_derived_class(), a reference to the value type of the data member is returned, and most_derived_object is set to the same value as object.
Example: class hierarchy
Consider, for example, the following class hierarchy:
definitions
class C0 { public: int i0; }; class B0 { public: void f0(); }; class B1 : public B0 { public: virtual void f1(); C0 c0; }; class C1 : public B1 { public: static os_typespec* get_os_typespec(); int i1; };Some compilers will optimize B0 so that it has zero size in B1 (and C1). This means the class-valued data member c0 is collocated with a base class, B0, of the class, C1, that defines the data member.
Given
C1 c1; C1 * pc1 = & c1; B0 * pb0 = (B0 *)pc1; C0 * pc0 = & pc1->c0;the pointers pb0 and pc0 will have the same value, because of this optimization.
Function result
In this case get_most_derived_class() called on the pb0 or pc0 will return a reference to the os_class_type for C0 (the value types of the data member c0) and most_derived_object are set to the same value as object.
class B1 : public B0 { public: virtual void f1(); int i; C0 c0; };and
int * pi = & pc1->i;pb0 and pi have the same value because of the optimization, but the base class, B0, is collocated with an int-valued data member rather than a class-valued data member. get_most_derived_class() called on pb0 or pi returns a pointer to the os_class_type for the class C1 and sets most_derived_object to the same value as pc1.
See os_base_class in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Attributes of
os_base_class
Create Functions
This class has one create function:
static os_base_class &create ( os_unsigned_int32 access , os_boolean is_virtual , os_class_type *associated_class ) ;
To represent the derivation of mechanical_part from part, you might create an os_base_class as follows:
os_class_type *the_class_part = ... os_class_type *the_class_mechanical_part = ... . . . os_base_class & a_base = os_base_class::create ( os_base_class::Public , 0, os_class_type *the_class_part ) ; os_List<os_base_class*> bases ; bases |= a_base ; the_class_mechanical_part->set_base_classes ( bases ) ;
const os_class_type &get_class() const ; os_class_type &get_class() ;If no class was specified when the os_base_class was created (that is, if the associated class argument to create() was 0), the unspecified type is returned. This is the only os_type for which os_type::is_unspecified() returns nonzero (see The is_unspecified() function).
Setting the attribute
You can set the class of an os_base_class with
void set_class(os_class_type&) ;
os_base_class::Public os_base_class::Private os_base_class::Protected
os_unsigned_int_32 get_access() const ;
void set_access(os_unsigned_int_32) ;
os_boolean is_virtual() const ;
void set_is_virtual(os_boolean) ;
See os_member in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Some member functions of
os_member
Class os_member and its subclasses
This class has no direct instances. Every instance of os_member is a direct instance of one of the subclasses of os_member.
Create Functions
Since this class has no direct instances, it has no create function. You create an instance of os_member with a create function for one of the subclasses of os_member. The access Attribute
Attribute enumerators
The access of a given os_member is one of the following enumerators:
os_member::Public os_member::Private os_member::Protected
os_unsigned_int32 get_access() const ;
void set_access(os_unsigned_int32) ;If the specified os_unsigned_int32 does not have the same value as one of the above enumerators, err_mop is signaled.
Each of these enumerators corresponds to a subclass of os_member.
Initialization
The value of kind for a given os_member is initialized to the enumerator corresponding to the subclass of which the os_member is a direct instance. This initialization is performed by the create function for the subclass. Getting the attribute
You can get the kind of an os_member with
os_unsigned_int32 get_kind() const ;
const os_class_type &get_defining_class() const ; os_class_type &get_defining_class() ;
Setting the attribute
The defining_class of an os_member is automatically set when os_class_type::set_members() is passed a collection containing a pointer to the os_member. The os_member's defining_class is set to the os_class_type for which set_members() is called. Type-Safe Conversion Operators
Like the class os_type, os_member defines type-safe conversion operators for converting const os_members to const references to an instance of a subtype.
operator const os_member_variable&() const ; operator const os_field_member_variable&() const ; operator const os_relationship_member_variable&() const ; operator const os_member_function&() const ; operator const os_access_modifier&() const ; operator const os_member_type&() const ;There are also type-safe conversion operators for converting non-const os_members to non-const references to an instance of a subtype.
operator os_member_variable&() ; operator os_field_member_variable&() ; operator os_relationship_member_variable&() ; operator os_member_function&() ; operator os_access_modifier&() ; operator os_member_type&() ;If an attempt is made to perform an inappropriate conversion, err_mop_illegal_cast is signaled.
See os_member_variable in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_member_variable &create ( const char *name , os_type *value_type ) ;
The initial values for the remaining attributes are as follows:
Attribute | Value |
---|---|
storage_class | os_member_variable::Regular |
is_field | 0 |
is_static | 0 |
is_persistent | 0 |
The name Attribute
The name of an os_member_variable is its unqualified name (for example, components rather than part::components). Getting the attribute
You can get the value of name with
const char *get_name() const ;
void set_name(const char *name) ;
const os_type &get_type() const ; os_type &get_type() ;
void set_type(os_type&) ;
os_member_variable::Regular os_member_variable::Persistent os_member_variable::Static
os_unsigned_int32 get_storage_class() const ;
void set_storage_class(os_unsigned_int32) ;
os_boolean is_field() const ;
os_boolean is_static() const ;
os_boolean is_persistent() const ;
operator const os_field_member_variable&() const ; operator const os_relationship_member_variable&() const ;There are also type-safe conversion operators for converting non-const os_member_variables to non-const references to an instance of a subtype.
operator os_field_member_variable&() ; operator os_relationship_member_variable&() ;These functions signal err_mop_illegal_cast if an attempt is made to perform an inappropriate conversion.
See os_relationship_member_variable in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_relationship_member_variable &create ( const char *name , const os_type *value_type, const os_class_type *related_class, const os_relationship_member_variable *related_member ) ;
The initial values for the remaining attributes are as described for os_member_variable::create().
const os_class_type &get_related_class() const ; os_class_type &get_related_class() ;
void set_related_class(os_class_type&) ;
const os_relationship_member_variable & get_related_member() const ; os_relationship_member_variable &get_related_member() ;
void set_related_member(os_relationship_member_variable&) ;
See os_field_member_variable in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Functions
This class has one create function:
static os_field_member_variable &create ( const char *name , const os_type *value_type, os_unsigned_int8 size_in_bits ) ;
The initial values for the remaining attributes are as described for os_member_variable::create().
os_unsigned_int8 get_size() const ;
void set_size(os_unsigned_int8) ;
See os_access_modifier in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_access_modifier &create(os_member*) ;
const os_member &get_base_member() const ; os_member &get_base_member() ;
void set_base_member(os_member&) ;
See os_enum_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_enum_type &create ( const char *name, const os_List<os_enumerator_literal*> &enumerators ) ;
const char *get_name() const ;
void set_name(const char *name) ;
os_List<const os_enumerator_literal*> get_enumerators() const ; os_List<os_enumerator_literal*> get_enumerators() ;
void set_enumerators(const os_List<os_enumerator_literal*>&) ;
See os_enumerator_literal in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_enumerator_literal &create ( const char *name, os_int32 value ) ;
const char *get_name() const ;
void set_name(const char *name) ;
See os_void_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class's create function is
static os_void_type &create() ;
See os_pointer_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
The create function for this class is
static os_pointer_type &create(os_type *target_type) ;
const os_type &get_target_type() const ; os_type &get_target_type() ;If no type was specified when the os_pointer_type was created (that is, if the class argument to create() was 0), the unspecified type is returned. This is the only os_type for which os_type::is_unspecified() returns nonzero (see The is_unspecified() function).
Setting the attribute
You can set the target_type with
void set_target_type(os_type&)This attribute is initialized with the create function.
operator const os_pointer_to_member_type&() const ; operator const os_reference_type&() const ;There are also type-safe conversion operators for converting non-const os_pointer_types to non-const references to an instance of a subtype.
operator os_pointer_to_member_type&() const ; operator os_reference_type&() ;These functions signal err_mop_illegal_cast if an attempt is made to perform an inappropriate conversion.
See os_reference_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
The create function for this class is
static os_reference_type &create(os_type *target_type) ;
See os_pointer_to_member_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
The create function for this class is
static os_pointer_to_member_type &create ( os_type *target_type, os_class_type *target_class ) ;
const os_class_type &get_target_class() const ; os_class_type &get_target_class() ;If no class was specified when the os_pointer_to_member_type was created (that is, if the target_class argument to create() was 0), the unspecified type is returned. This is the only os_type for which os_type::is_unspecified() returns nonzero (see The is_unspecified() function).
Setting this attribute
You can set the target_class with
void set_target_class(os_class_type&)
See os_named_indirect_type (page 247) and os_anonymous_indirect_type (page 249).
See also os_indirect_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
The Class os_named_indirect_type
Attribute of os_named_indirect_type
An instance of this class represents a C++ typedef. The diagram below shows the attribute target_type, inherited from os_indirect_type, but it does not show attributes inherited from os_type.
Create Function
The create function for this class is
static os_named_indirect_type &create ( os_type *target_type, const char *name ) ;
const os_type &get_target_type() const ; os_type &get_target_type() ;
void set_target_type(os_type&)
const char *get_name() const ;
void set_name(const char *name) ;
See os_anonymous_indirect_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
The create function for this class is
static os_anonymous_indirect_type &create ( os_type *target_type ) ;
const os_type &get_target_type() const ; os_type &get_target_type() ;
void set_target_type(os_type&)
os_boolean is_const() const ;
void set_is_const(os_boolean) ;
os_boolean is_volatile() const ;
void set_is_volatile(os_boolean) ;
See os_array_type in Chapter 2, Class Library, of the ObjectStore C++ API Reference for a complete description of this class.
Create Function
This class has one create function:
static os_array_type &create( os_unsigned_int32 number_of_elements, os_type *element_type ) ;
os_unsigned_int32 get_number_of_elements() const ;
void set_number_of_elements(os_unsigned_int32) ;
const os_type &get_element_type() const ; os_type &get_element_type() ;
void set_element_type(os_type&) ;
The first parameter to functions in the os_fetch and os_store classes is a void pointer to an arbitrary object. This pointer must refer to the closest containing class. If, for example, the relevant member variable being accessed is part of an inherited class, you must pass the address of the base class, not the outermost class. If you pass the wrong pointer, you will end up accessing the wrong address.
The first parameter to os_fetch and os_store is a void pointer to an arbitrary object. This pointer must refer to the closest containing class. If, for example, the relevant member variable being accessed is part of an inherited class, you must pass the address of the base class, not the outermost class. If you pass the wrong pointer, you will end up accessing the wrong address.
For more information, see ::os_fetch() in Chapter 3, System-Supplied Global Functions, of the ObjectStore C++ API Reference.
void* os_fetch( const void *p, const os_member_variable&, void *&value); unsigned long os_fetch( const void *p, const os_member_variable&,
unsigned long &value); long os_fetch( const void *p, const os_member_variable&, long &value); unsigned int os_fetch( const void *p, const os_member_variable&,
unsigned int &value); int os_fetch( const void *p, const os_member_variable&, int &value); unsigned short os_fetch( const void *p, const os_member_variable&,
unsigned short &value); short os_fetch( const void *p, const os_member_variable&, short &value); unsigned char os_fetch( const void *p, const os_member_variable&,
unsigned char &value); char os_fetch( const void *p, const os_member_variable&, char &value); float os_fetch( const void *p, const os_member_variable&, float &value); double os_fetch( const void *p, const os_member_variable&, double &value); long double os_fetch( const void *p, const os_member_variable&, long double &value);
void os_store( void *p, const os_member_variable&, const void *value); void os_store( void *p, const os_member_variable&,
const unsigned long value); void os_store( void *p, const os_member_variable&, const long value); void os_store( void *p, const os_member_variable&, const unsigned int value); void os_store( void *p, const os_member_variable&, const int value); void os_store( void *p, const os_member_variable&, const unsigned short value); void os_store( void *p, const os_member_variable&, const short value); void os_store( void *p, const os_member_variable&, const unsigned char value); void os_store( void *p, const os_member_variable&, const char value); void os_store( void *p, const os_member_variable&, const float value); void os_store( void *p, const os_member_variable&, const double value); void os_store( void *p, const os_member_variable&, const long double value);
void *a_part_ptr = ::operator new( the_class_part.get_size(), db, &part_typespec ) ;You can use the function ::os_store() to initialize the new instance.
#include <ostore/mop.hh> const os_unsigned_int32 max_buff_size = 100 ; static void print(const void* p, const os_class_type& c, char* member_prefix = "", os_unsigned_int8 indentation = 0 { if (!*member_prefix) fprintf(stdout, "\n%sclass %s /* 0x%x */ {\n", indent(indentation), c.get_name(), p) ; os_Cursor<const os_base_class*> bc(c.get_base_classes()) ; for (const os_base_class* b=bc.first(); b ; b = bc.next()) { char buff[1024] ; os_strcpy(buff, member_prefix) ; os_strcat(buff, b->get_class().get_name()) ; os_strcat(buff, "::") ; print((void*) ((char*)p+b->get_offset()), b->get_class(), buff, indentation) ; } /* end of for loop */ os_Cursor<const os_member*> mc(c.get_members()) ; for (const os_member* m=mc.first(); m ; m=mc.next()) switch (m->kind()) { case os_member::Variable: case os_member::Relationship: case os_member::Field_variable: { const os_member_variable& mv = *m ; char* type_string = mv.get_type().get_string() ; if (mv.is_static() || mv.is_persistent()) continue ; fprintf(stdout, "%s %s\t%s%s = ", indent(indentation), type_string, member_prefix, mv.get_name()) ; print(p, mv, indentation) ; fprintf(stdout, " ;\n") ; delete [] type_string ; break ; } /* end of case statement*/ } /* end of for loop */ if (!*member_prefix) fprintf(stdout, "%s}", indent(indentation)) ; } /* end of print() function */Let us explain the function by considering some sample input.
class date { private: int day; int month; int year; public: date(int dd, int mm, int yy) { day = dd; month = mm; year = yy; } . . . }; class part { private: int part_id; date date_created; public: part(int id, date d) {part_id = id; date_created = d;} . . . }; class mechanical_part : public part { private: mechanical_part *parent; public: mechanical_part(int id, date d, mechanical_part *p) : part(id, d) {parent = p;} . . . };
date d(1, 15, 1993); mechanical_part *parent = new(db) mechanical_part(1, d, 0); mechanical_part *child = new(db) mechanical_part(2, d, parent);
print(child, os_type::type_at(child));print() begins with a check of the argument member_prefix, which defaults to a pointer to the null character (0). Since this is 0, you have just started printing an object, and so the function outputs the name of the object's class with a call to os_class_type::get_name():
c.get_name()The object's address is also printed.
class mechanical_part /* 0xCB320 */ {
c.get_base_classes()For each base class, the function prints the portion of the specified object that corresponds to that base class. It does this by calling itself recursively, specifying the address of the appropriate subobject, and specifying the base type as its type.
How the object's address is obtained
The address of the appropriate object is obtained by adding the base type's offset to p, the address of the original object. The offset is obtained using os_base_class::get_offset().
The type is obtained using os_base_class::get_class(). Remember, an os_base_class encapsulates information about the derivation of one class from another (for example, the offset of the base class within instances of the derived class - which you just used). An os_base_class is not itself an os_class_type. To get the associated os_class_type object, you use get_class().
Iterating through the base classes of the specified class
Next you iterate through the base classes of the specified class, part in this case. This takes care of subobjects corresponding to indirect base classes. Since part itself has no base classes, this loop is null for the sample input. Iterating through members of the specified class
Then you iterate through the collection of members of the specified class, obtained with a call to os_class_type::get_members():
c.get_members()Since you are within the recursive execution, the specified class is part. You test the kind of each member using os_member::kind(), and for each nonstatic, nonpersistent data member, you output the data member's name (using member_prefix) and type, and call an overloading of print() that prints data member values (described below).
const os_member_variable &mv = *m;Recall that there are type-safe conversions from os_member to const os_member_variable&, as well as to all the other subtypes of os_member.
You get the value type of the member using os_member_variable::get_type() and you get the name of this type using os_type::get_string():
char *type_string = mv.get_type().get_string()Note that get_string() allocates a character array, which is deleted when no longer needed:
delete [] type_string;
class mechanical_part /* 0xCB320 */ { int part::part_id =
print(p, mv, indentation);Here is how this function is defined:
/* Prints the value at p. It is the value of the data member */ /* indicated by the "m" argument */ static void print(const void* p, const os_member_variable& m, const os_unsigned_int8 indentation) { const os_type& mt = m.get_type().strip_indirect_types() ; if (mt.is_integral_type()) { if (((const os_integral_type&)mt).is_signed()) { os_int32 value ; fprintf(stdout, "%ld", os_fetch(p, m, value)) ; } /* end if */ else { os_unsigned_int32 value ; fprintf(stdout, "%lu", os_fetch(p, m, value)) ; } /* end else */ return ; } /* end if */ else if (mt.kind()==os_type::Enum) { os_int32 value = 0 ; const os_enumerator_literal* lit= ((os_enum_type&)mt).get_enumerator(
os_fetch(p, m, value)) ; fprintf(stdout, "%ld(%s)", value, (lit ? lit->get_name() : "?enum literal?" )) ; return ; } /* end else if */ switch (mt.kind()) { case os_type::Float: { float value ; fprintf(stdout, "%f", os_fetch(p, m, value)) ; return ; } case os_type::Double: { double value ; fprintf(stdout, "%lg", os_fetch(p, m, value)) ; return ; } case os_type::Long_double: { long double value ; fprintf(stdout, "%lg", os_fetch(p, m, value)) ; return ; } case os_type::Pointer: case os_type::Reference: print_a_pointer((char*)p+m.get_offset()) ; return ; case os_type::Class: case os_type::Instantiated_class: print((char*)p+m.get_offset(), (const os_class_type&)mt, "", indentation+1) ; return ; case os_type::Array: print((char*)p+m.get_offset(), (const os_array_type&)mt, indentation+1) ; return ; default: /* print its address */ fprintf(stdout, "*0x%x*", (char*)p + m.get_offset()) ; } /* end switch */ }
For the sample input, the member is currently part::part_id and the value type is int. This is a signed integer type, so the value is printed with the format "%ld". The value is obtained with os_fetch():
os_fetch(p, m, value);
class mechanical_part /* 0xCB320 */ { int part::part_id = 2For the next member, part::date_created, the output is supplemented to look like
class mechanical_part /* 0xCB320 */ { int part::part_id = 2 date date_created =before print() for member values is called again.
print((char*)p+m.get_offset(), (const os_class_type&)mt, "", indentation+1) ;The arguments are a pointer to the data member value (obtained with the help of os_member_variable::get_offset()) and the member's value type (which you cast to a const os_class_type&).
This call to print() supplements the output with a representation of the date object that serves as the data member value:
class mechanical_part /* 0xCB320 */ { int part::part_id = 2 date part::date_created = class date /* 0xCB324 */ { int day = 1 int month = 1 int year = 1993 }
class mechanical_part /* 0xCB320 */ { int part::part_id = 2 date part::date_created = class date /* 0xCB324 */ { int day = 1 int month = 1 int year = 1993 } part* parent =and then calls print() for data members.
/* print a pointer value along with as much useful info as possible */ static void print_a_pointer(const void** p) { static os_type::os_type_kind string_char = char(0x80) > 0 ? os_type::Signed_char os_type::Unsigned_char ; if (*p) { const os_type* type = os_type::type_at(*(void**)p) ; char* tstr = type ? type->get_string() : os_strdup("???") ; fprintf(stdout, "(%s*)%#lx%s", tstr, (unsigned long)*(void**)p, ((type && (type->kind() == string_char)) ? get_string((char*)*p, string_char) : "")) ; delete tstr ; const void* op = 0 ; os_unsigned_int32 ecount = 0 ; const os_type* otype = os_type::type_containing( *p, op, ecount) ; if (op && (op != *p) && (otype != type)) { /* the enclosing object is different */ if (ecount > 1) { /* point to the appropriate array element */ os_unsigned_int32 offset = (char*)op - (char*)*p ; os_unsigned_int32 i = offset / otype->get_size() ; op = (char*)op + (i * otype->get_size()) ; } /* end if */ char* tstr = type ? otype->get_string() : os_strdup("???") ; fprintf(stdout, " /* enclosing object @ (%s*)%#lx */ ", tstr, (unsigned long)op) ; delete tstr ; } /* end if */ } /* end if */ else fprintf(stdout, "0") ; } /* end print_a_pointer() */print_a_pointer() prints the specified pointer's type and value, and, if the pointer is a char*, it prints up to the first 100 characters of the designated string (with the help of get_string(), shown below). In addition, if the object pointed to is embedded in some other object or array, the type and address of the enclosing object are printed.
class mechanical_part /* 0xCB322 */ { int part::part_id = 2 date part::date_created = class date /* 0xCB326 */ { int day = 1 int month = 1 int year = 1993 } part* parent = (part*) 0xCB300 }
static char* get_string(const char* p, os_type:: os_type_kind string_char) { const void* op = 0 ; os_unsigned_int32 ecount = 0 ; /* Ignore embedded strings for now */ const os_type* otype = os_type::type_containing(p, op, ecount) ; /* +5 for the quotes + null character*/ static char buff[max_buff_size+5]; char *bp = buff ; os_strcpy(bp, " \"") ; bp += 2 ; if (op && otype && (otype->kind() == string_char)) { ecount=ecount-(p-(char*)op); /*in case it is pointing into the middle */ os_unsigned_int32 count = (ecount <= max_buff_size)? ecount : max_buff_size ; for (; count && (*bp = *p); bp++, p++, count--) ; if (*p) { /* determine its true length */ count = ecount - max_buff_size ; for (; count && (*p); p++, count--) ; ecount -= count ; os_sprintf(bp-(3+10+3), "...%d...", ecount) ; bp = buff + os_strlen(buff) ; } /* end if */ os_strcpy(bp, "\" ") ; } /* end if */ else buff[0] = 0 ; return buff ; } /* end of get_string() */
static void print(const void* p, const os_array_type& at, const os_unsigned_int8 indentation) { const os_type& element_type = at.get_element_type().strip_indirect_types(); fprintf(stdout, " { ") ; for (int i = 0; i < at.number_of_elements(); i++, p = (char*)p + element_type.get_size()) { print(p, element_type, indentation) ; fprintf(stdout, "%s", (i+1) == at.number_of_elements() ? " }" : ", ") ; } /* end of for loop */ }
static void print(const void* p, const os_type& et, const os_unsigned_int8 indentation) { switch (et.kind()) { case os_type::Unsigned_char: fprintf(stdout, "%lu", (os_unsigned_int32)*
(unsigned char*)p) ; break ; case os_type::Signed_char: fprintf(stdout, "%ld", (os_int32)*(char*)p) ; break ; case os_type::Unsigned_short: fprintf(stdout, "%lu", (os_unsigned_int32)*
(unsigned short*)p) ; break ; case os_type::Signed_short: fprintf(stdout, "%ld", (os_int32)*(short*)p) ; break ; case os_type::Integer: fprintf(stdout, "%ld", (os_int32)*(int*)p) ; break ; case os_type::Enum: case os_type::Unsigned_integer: fprintf(stdout, "%lu", (os_unsigned_int32)*
(unsigned int*)p) ; break ; case os_type::Signed_long: fprintf(stdout, "%ld", (os_int32)*(int*)p) ; break ; case os_type::Unsigned_long: fprintf(stdout, "%lu", (os_unsigned_int32)*
(unsigned int*)p) ; break ; case os_type::Float: fprintf(stdout, "%f", *(float*)p) ; break ; case os_type::Double: fprintf(stdout, "%lg", *(double *)p) ; break ; case os_type::Long_double: fprintf(stdout, "%lg", *(long double *)p) ; break ; case os_type::Pointer: case os_type::Reference: print_a_pointer((void**)p) ; break ; case os_type::Array: print(p, (const os_array_type &)et, indentation) ; break ; case os_type::Class: case os_type::Instantiated_class: print(p, (const os_class_type&)et, "", indentation+1) ; return ; default: /* a type we do not understand how to print */ fprintf(stdout, "?%s?", et.kind_string(et.kind())) ; break ; } /* end of switch */ }
static const char* indent(os_unsigned_int32 ilevel) { static char indent_string[256] ; static os_unsigned_int32 maxilevel = 0, cilevel = 0 ; const os_unsigned_int32 indent_tab = 3 ; if (ilevel > (256/indent_tab)) ilevel = 256/indent_tab ; if (ilevel <= maxilevel) { indent_string[cilevel*indent_tab]= ` ` ; indent_string[ilevel*indent_tab]= 0 ; cilevel = ilevel ; return indent_string ; } /* end if */ os_unsigned_int32 limit = ilevel * indent_tab ; for (os_unsigned_int32 i = maxilevel*indent_tab; i < limit; i++) indent_string[i] = ` ` ; maxilevel = cilevel = ilevel ; return indent_string ; }
The arcs (represented as arrows in the diagram) have single or double arrows at one or both ends. Here is what the arrows mean:
class part { public: int part_id ; os_collection &components ; os_collection &resp_engs ; } ; class employee { public: department *head_of ; department *dept ; os_collection &part_resp_for ; } ; class department { public: employee *dept_head ; os_collection &emps ; } ;Note that if a single arrow points to a node with a class name as label, a pointer to that class is used as the value type of the corresponding data member. This is a simple way to prevent circular dependencies (assuming arrays of classes are not used). Note also that double arrows correspond to data members whose value type is os_collection&. A future release will support the dynamic creation of parameterized types, so it will be possible to use, for example, os_Collection<part*>&, instead of os_collection&.
/* graph.hh */ #include <string.h> enum end_enum { no_arrow, single_arrow, double_arrow } ; class node { public: char *label ; static os_typespec *get_os_typespec() ; node ( char *l ) ; }; class arc { public: node *node_1 ; node *node_2 ; end_enum end_1 ; end_enum end_2 ; char *label_1 ; char *label_2 ; static os_typespec *get_os_typespec() ; arc ( node *n1, node *n2, end_enum e1, end_enum e2, char *l1, char *l2 ) ; };
/* graph.cc */ #include <ostore/ostore.hh> #include "graph.hh" arc::arc ( node *n1, node *n2, end_enum e1, end_enum e2, char *l1, char *l2 ) { node_1 = n1; node_2 = n2; end_1 = e1; end_2 = e2; if (l1) { label_1 = new( os_segment::of(this), os_typespec::get_char(), strlen(l1) + 1 ) char[strlen(l1) + 1]; strcpy(label_1, l1); } /* end if */ else label_1 = 0; if (l2) { label_2 = new( os_segment::of(this), os_typespec::get_char(), strlen(l2) + 1 ) char[strlen(l2) + 1]; strcpy(label_2, l2); } /* end if */ else label_2 = 0; } node::node ( char *l ) { label = new( os_segment::of(this), os_typespec::get_char(), strlen(l) + 1 ) char[strlen(l) + 1]; strcpy(label, l); }
ensure_in_trans() determines whether there is a type in the transient schema whose name is the node's label. If there is not, it determines whether there is a type in the application schema whose name is the node's label. If there is, ensure_in_trans() copies it to the transient schema (using copy_to_trans()). If there is not, ensure_in_trans() creates a class with that name. It returns a pointer to the newly created, copied, or retrieved type.
Copying types from the application schema to the transient schema is the typical means of getting ObjectStore system-supplied classes into the transient schema. Note, however, that built-in C++ types, like int, are already present in the transient schema.
Notice also that lookups in the application schema and copying from the application schema must be performed within a transaction, since they are operations on a database (the application schema database).
Next gen_schema() determines which of the following eight cases applies to the arc at hand:
Each data member is created as well as added to the appropriate defining class. This is accomplished by add_single_valued_data_member() or add_many_valued_member(). Each of these functions creates a data member and then calls add_member(). add_member() adds a specified member to a specified class.
Once gen_schema() finishes processing all the arcs, the transient schema contains the schema represented by the diagram.
/* gen_schema.cc */ #include <ostore/ostore.hh> #include <ostore/coll.hh> #include <ostore/mop.hh> #include <stdlib.h> #include <iostream.h> #include <assert.h> #include "graph.hh" void error(char *m) { cout << m << "\n" ; exit (1) ; }
void add_member(os_class_type &defining_class, os_member &new_member) { os_List<os_member*> members( defining_class.get_members() ); members |= &new_member ; defining_class.set_members(members) ; }Notice that os_class_type::get_members() returns an os_List<os_member*>. To add or remove a member, copy the returned list and update the copy. Then pass the list to os_class_type::set_members().
os_type *copy_to_trans(const char *class_name) { OS_BEGIN_TXN(tx1, 0, os_transaction::update) const os_type *the_const_type_ptr = os_app_schema::get().find_type(class_name) ; if (!the_const_type_ptr) return 0 ; const os_class_type &the_const_class = *the_const_type_ptr ; os_Set<const os_class_type*> to_be_copied_to_transient_schema ; to_be_copied_to_transient_schema |= &the_const_class ; os_mop::copy_classes ( os_app_schema::get(), to_be_copied_to_transient_schema ) ; OS_END_TXN(tx1) return os_mop::find_type(class_name) ; }Notice that os_mop::copy_classes() requires an os_Set<const os_class_type*>. In order to create this set with the appropriate contents, this function first retrieves a const os_type*, dereferences it, and converts it to a const os_class_type&. The const os_class_type& is then dereferenced and inserted into an os_Set<const os_class_type*>. Next, this set is passed to os_mop::copy_classes(), which copies the set's element into the transient schema.
Finally, os_mop::find_type() is used to retrieve from the transient schema a (non-const) os_type*, which is returned. The function does not simply return the_const_type_ptr, because copy_to_trans() should return a modifiable object, one to which you can add members. Looking up a class in any schema except the transient schema results in a const os_type*. Only a lookup in the transient schema results in a non-const os_type*.
os_type &ensure_in_trans(const char *type_name){ os_type *t = os_mop::find_type(type_name) ; if (!t) t = copy_to_trans(type_name) ; if (!t) { os_class_type &c = os_class_type::create(type_name) ; c.set_is_forward_definition(0) ; c.set_is_persistent(1) ; t = &c ; } /* end if */ return *t ; }When you create a class, the attribute is_forward_definition defaults to true. Here it is set to false after creation, because gen_schema() will generate a class definition for each node that represents a class. Similarly, is_persistent defaults to false. Here, it is set to true so the new class can be installed in a database schema.
void add_single_valued_member( os_class_type &defining_class, os_type &value_type, const char *member_name ) { if (!member_name) error("unspecified member name") ; os_member_variable &new_member = os_member_variable::create( member_name, &value_type ) ; add_member(defining_class, new_member) ; }Note that while the formal parameter defining_class is of type os_class_type&, the corresponding actual parameter can be typed as os_type&. If you pass in such an actual parameter, MOP invokes os_type::operator os_class_type&(), which converts the actual parameter to an os_class_type&. If the object designated by the actual parameter is not really an instance of os_class_type, the operator signals err_mop_illegal_cast.
void add_many_valued_member( os_class_type &defining_class, const char *member_name ) { if (!member_name) error("unspecified member name") ; os_type *the_type_os_collection_ptr = os_mop::find_type("os_collection") ; if (!the_type_os_collection_ptr) the_type_os_collection_ptr = copy_to_trans("os_collection") ; if (!the_type_os_collection_ptr) error("Could not find the class os_collection in the \ application schema") ; os_member_variable &new_member = os_member_variable::create( member_name, &os_reference_type::create(the_type_os_collection_ptr) ) ; add_member(defining_class, new_member) ; }This function copies the class os_collection from the application schema to the transient schema, if it is not already present in the transient schema.
void gen_schema(const os_Collection<arc*> &arcs) { /* process each arc in the graph */ os_Cursor<arc*> c(arcs) ; for (arc *a = c.first(); a; a = c.next()) { os_type &t1 = ensure_in_trans(a->node_1->label) ; os_type &t2 = ensure_in_trans(a->node_2->label) ; /* handle 1 of 8 cases, depending on arc's arrows */ if ( a->end_1 == no_arrow && a->end_2 == single_arrow ) if (t2.get_kind() != os_type::Class) add_single_valued_member( t1, /* defining type */ t2,/* value type */ a->label_2 /* member with value type t2 */ ) ; else add_single_valued_member( t1, /* defining type */ os_pointer_type::create(&t2), /* value type */ a->label_2 /* of member with value type t2 */ ) ; else if ( a->end_1 == single_arrow && a->end_2 == no_arrow ) if (t1.get_kind() != os_type::Class) add_single_valued_member( t2, /* defining type */ t1, /* value type */ a->label_1 /* member with value type t1 */ ) ; else add_single_valued_member( t2, /* defining type */ os_pointer_type::create(&t1), /* value type */ a->label_1 /*member with value type t1 */ ) ; else if ( a->end_1 == no_arrow && a->end_2 == double_arrow ) add_many_valued_member( t1, /* defining type */ a->label_2 /* name of many-valued member */ ) ; else if ( a->end_1 == double_arrow && a->end_2 == no_arrow ) add_many_valued_member( t2, /* defining type */ a->label_1 /* name of many-valued member */ ) ; else if ( a->end_1 ==single_arrow && a->end_2 == single_arrow ) { /* binary relationship */ add_single_valued_member( t1, /* defining type */ os_pointer_type::create(&t2), /* value type */ a->label_2 /* member with value type t2 */ ) ; add_single_valued_member( t2, /* defining type */ os_pointer_type::create(&t1), /* value type */ a->label_1 /* member with value type t1 */ ) ; } /* end of else if */ else if ( a->end_1 == single_arrow && a->end_2 == double_arrow ) { /* binary relationship */ add_single_valued_member( t2, /* defining type */ os_pointer_type::create(&t1), /* value type */ a->label_1 /* member with value type t1 */ ) ; add_many_valued_member( t1, /* defining type */ a->label_2 /* name of many-valued member */ ) ; } /* end of else if */ else if ( a->end_1 == double_arrow && a->end_2 == single_arrow ) { /* binary relationship */ add_single_valued_member( t1, /* defining type */ os_pointer_type::create(&t2), /* value type */ a->label_2 /* member with value type t2 */ ) ; add_many_valued_member( t2, /* defining type */ a->label_1 /* name of many-valued member */ ) ; } /* end of else if */ else if ( a->end_1 == double_arrow && a->end_2 == double_arrow ) { /* binary relationship */ add_many_valued_member( t1, /* defining type */ a->label_2 /* name of many-valued member */ ) ; add_many_valued_member( t2, /* defining type */ a->label_1 /* name of many-valued member */ ) ; } /* end of else if */ } /* finish processing arcs (for loop)*/ }
The driver relies on two functions, find_class() and find_member(), to instantiate the dynamically created classes. These supporting functions are shown first.
find_class() function definition
This function returns a reference to the class in the_schema with name class_name. If the class is not found, the function returns an error. If class_name is not a class, err_mop_illegal_cast is signaled.
const os_class_type &find_class( const char *class_name, const os_schema &the_schema ) { const os_type *the_type_ptr = the_schema.find_type(class_name) ; if (!the_type_ptr) error("Cannot find class with specified name") ; return *the_type_ptr ; }
const os_member_variable &find_member_variable( const os_class_type &defining_class, const char *member_name ) { const os_member *the_member = defining_class.find_member(member_name) ; if (!the_member) error("Could not find member with specified name.") ; return *the_member ; }
void main(int, char **argv) { objectstore::initialize() ; os_collection::initialize() ; os_mop::initialize() ; if (!argv[1]) error("null database name\n") ; /* create a graph representing an entity-relationship diagram */ /* the graph is a collection of arcs */ os_Collection<arc*> &arcs = os_Collection<arc*>::create( os_database::get_transient_database() ) ; node *part_node = new node("part") ; node *employee_node = new node("employee") ; node *int_node = new node("int") ; node *department_node = new node("department") ; arcs |= new arc( part_node, int_node, no_arrow, single_arrow, 0, "part_id" ) ; arcs |= new arc( part_node, part_node, no_arrow, double_arrow, 0, "components" ) ; arcs |= new arc( employee_node, department_node, single_arrow, single_arrow, "dept_head", "head_of" ) ; arcs |= new arc( employee_node, department_node, double_arrow, single_arrow, "emps", "dept" ) ; arcs |= new arc( part_node, employee_node, double_arrow, double_arrow, "parts_resp_for", "resp_engs" ) ; cout << "Calling gen_schema() ...\n" ; gen_schema(arcs) ; cout << "Schema generated. Installing schema ...\n" ; os_database *db = os_database::open(argv[1], 0, 0664) ; /* install schema in db */ OS_BEGIN_TXN(tx1, 0, os_transaction::update) os_database_schema::get_for_update(*db).install( os_mop::get_transient_schema() ) ; OS_END_TXN(tx1) OS_BEGIN_TXN(tx2, 0, os_transaction::update) /* create a part, an employee, and a department */ /* and partially initialize them */ os_typespec part_typespec("part") ; os_typespec employee_typespec("employee") ; os_typespec department_typespec("department") ; const os_database_schema &the_database_schema = os_database_schema::get(*db) ; const os_class_type &the_class_part = find_class( "part", the_database_schema ) ; const os_class_type &the_class_employee = find_class( "employee", the_database_schema ) ; void *a_part_ptr = ::operator new( the_class_part.get_size(), db, &part_typespec ) ; void *an_emp_ptr = ::operator new( the_class_employee.get_size(), db, &employee_typespec ) ; void *a_dept_ptr = ::operator new( find_class( "department", the_database_schema ).get_size() db, &department_typespec ) ; os_collection &the_components_coll = os_collection::create( os_segment::of(a_part_ptr) ) ; os_collection &the_resp_engs_coll = os_collection::create( os_segment::of(a_part_ptr) ) ; the_resp_engs_coll |= an_emp_ptr ; os_store( a_part_ptr, find_member_variable(the_class_part, "part_id"), 1 ) ; os_store( a_part_ptr, find_member_variable(the_class_part, "resp_engs"), &the_resp_engs_coll ) ; os_store( a_part_ptr, find_member_variable(the_class_part, "components"), &the_components_coll ) ; os_store( an_emp_ptr, find_member_variable(the_class_employee, "dept"), a_dept_ptr ) ; db->create_root("part_root")->set_value(
a_part_ptr, &part_typespec ) ; OS_END_TXN(tx2) db->close() ; cout << "Done.\n" ; }
The driver instantiates the classes part, employee, and department by calling the global function ::operator new() without a constructor call. The function os_type::get_size() is used to supply the size_t argument to ::operator new(). The function ::os_store() is used to partially initialize the instance of part.
You can run this program and use the Browser to verify that it produces the class definitions presented on page 270.
Updated: 03/31/98 15:29:41