Chapter 9: Object Sharing

 

Mixing C and C++ Code and Objects

The best way to handle objects created with C++/VERSANT is with C++ applications. The best way to handle objects created with C/VERSANT is with C applications.

To handle both C and C++ objects in the same application, the easiest way is to write in C++ and use the C++ interface to handle the C++ objects and simultaneously use the C Interface to handle the C objects. The only times that it makes sense to mismatch interfaces is when you want a C object to reference a C++ object, or vice versa.

Use the following approach to access C objects with the C++ Interface:

•    Include the C interface header file in the following manner:

extern "C" { #include "omapi.h" };      // right way

If you include the C interface header file as:

#include "omapi.h"                      // wrong way

..your C++ compiler will think that the C/VERSANT functions in omapi.h should have a C++ linkage, which will result in linker errors for each C/VERSANT function referenced.

Also, you should include cxxcls/pobject.h before omapi.h.

•    For a link, use LinkAny or o_object instead of Link<type>.

•    For vstrs, use either Vstr<type> or VstrAny.

•    For link vstrs, use LinkVstrAny instead of LinkVstr<type>.

•    To retrieve objects from a database, use the C function o_locateobj() instead of the C++ dereference operators ->, type*, or PObject*.

•    To acquire a write lock and mark an object for update, use o_preptochange() instead of the C++ PObject::dirty() method.

•    To modify an attribute, acquire a write lock, and mark an object for update, use o_setattr() instead of a C++ method.

•    To create a new C object, use o_createobj() rather than the C++ O_NEW_PERSISTENT() syntax.

•    To access C++ objects with the C Interface, use o_locateobj(), o_setattr(), and o_getattr() in a normal manner to retrieve and access C++ objects.

If your C++ object uses simple inheritance, you can use casting to a struct pointer to access the fields. Objects deriving from PObject have one attribute of type PClass* that will show at the top of your objects.

You cannot run the methods on an object created with the C Interface since the internal C++ pointers are not initialized properly.

The next section explains how to share objects among Smalltalk/VERSANT and C++/VERSANT applications.

 

Sharing Objects Among C++ and Smalltalk Applications

 

Object Sharing Procedures

To share objects among applications created with the Smalltalk/VERSANT and C++/VERSANT interfaces, you must do the following.

1.  Define classes using C++/VERSANT.

Use normal C++/VERSANT procedures to define classes that will have persistent instances stored in a database. Per normal requirements, these classes must derive from PObject or from a subclass of PObject.

2.  Import class definitions to your Smalltalk Image with the message #importSchema: to the ODBInterface class.

The general form of #importSchema: is:

importSchema: schemaName fromDB: dbName

...where schemaName is the the name of the class to be imported and dbName is the name of the database containing the class definition.

Changes to class definitions must be made with the C++/VERSANT interface.

If you change the database, attribute definition of a class after you have imported that class into your Smalltalk Image, you must reimport the class definition into your Smalltalk Image or else an error will be raised the next time you try to access an instance of that class. You must also take care when you access a class in a different database that has the same name but a different class defintion.

Smalltalk/VERSANT will disallow any changes to imported classes that will alter the number of instance variables or change the private class methods that are generated by Smalltalk/VERSANT.

In your Image, you can make the following changes to imported classes without creating and reimporting new definitions:

•    You can rename instance variables, as long as the total number of instance variables remains the same.

•    You can add and change methods except those that are generated by Smalltalk/VERSANT.

Information about imported classes is generated automatically and then internally accessed by the private class methods #vCxxSchemaSignature, #vDataAttributeNames, #vDataAttributePositions, #vDataAttributeRepeatFactors, #vDerivedStorageTransforms and #vFullSetAttributeNames.

 

Object Sharing Usage Notes

In most cases, once a class has been defined in C++/VERSANT and then imported into a Smalltalk Image, instances of that class can be created, updated, versioned, and deleted with applications using either C++ or Smalltalk. However, you do need to be aware of the following.

No multiple inheritance is allowed.

Since Smalltalk does not support multiple inheritance, you can only share objects among classes defined with single inheritance.

Most classes are flattened.

When class definitions are imported into Smalltalk, they are usually transformed to a flattened format (just like they are in the database). Exceptions are classes with C++/VERSANT VDate and VTime attributes: these attributes are mapped to instances of the Smalltalk Date and Timestamp classes.

Attribute values are restricted.

Attribute values must be restricted to value ranges supported by both Smalltalk and C++. This is explained in a following section.

Attribute definitions are checked, and changed classes must be reimported.

Each time you read an object from a database, VERSANT checks to make sure that the class attribute definitions in your Image corresponds to the definitions in the database.

By default, method signatures are not checked.

By default, VERSANT does not check method definitions when you read an object from a database. The default behavior allows you to create application specific methods for shared objects.

You can override the default and have VERSANT check to make sure that your Image has the methods as the database class by overwriting the #needSourceVerification method in your classes so that it returns true.

Importing can be done with a dialog box.

If you touch a reference to a persistent object whose class has not been defined in your Smalltalk Image or whose class definition has changed, Smalltalk/VERSANT will pop a dialog box asking for permission to import the class definition. For run-time applications, prior, explicit importing is usually preferable.

Imported classes will be placed under ODB-Imported Classes.

By default, imported classes are put under the ODB-Imported Classes category.

Imported classes will derive from Object.

When defined in C++/VERSANT, a persistent class will derive from either PObject or PVirtual. When imported, the class will still derive from PObject, but since in Smalltalk/VERSANT the PObject and PVirtual classes derive from the Smalltalk Object class, instances of an imported class will share all behavior of the Object class.

If the class derives from PObject, the PObject class will provide services for updating the database source or updating the imported class with the database source. The PVirtual class provides services for computing combined hash values expected for accessing or updating collection objects.

Handling of non-allowed characters.

C++ allows use of characters, such as [ ] < > and :, that are not allowed in Smalltalk. When classes are imported, Smalltalk/VERSANT will do the following:

1.  VERSANT will first try to use the leaf term as an instance variable name. For example, for VVSet<Employee>::Person.name, Smalltalk/VERSANT will use name as the instance variable.

2.  It may not be possible for Smalltalk/VERSANT to use the leaf term. For example, there may already be an instance variable with the same name as the leaf term. If Smalltalk/VERSANT must use a full name as defined in C++/VERSANT, it will convert non-allowed characters to underscores. For example, for VVSet<Employee>::Person.name, Smalltalk/VERSANT may find it necessary to use VVSet_Employee___Person.name.

Once a class has been imported, you can redefine the default instance variable names as you like, as long as the total number of instance variables is kept the same.

To refer to classes with non-allowed characters, you can do either of the following.

•    Refer to the class with a Smalltalk expression. For example, (Smalltalk at: #'VVSet<Employee>').

•    Assign the class to a global variable and then use the global variable as an alias to the class.

All C++/VERSANT collection classes may be used.

You can use Smalltalk/VERSANT to access and manipulate all C++/VERSANT collection classes if you implement hash functions in both C++ and Smalltalk that operate in the two environments in the same way. In other words, for each member, your hash function must return the same hash value in both C++ and Smalltalk. This requirement requires care, because native C++ and Smalltalk hash functions work differently.

You can compute an elemental hash value by sending #vElementalHash to an instance of Integer, Float, Double, Character, ByteString, or PObject. You can compute a combined hash value by using #vElementalCombinedHash:.. in the PVirtual class.

With these hashing methods, you can develop a sharable persistent collection that involves hash look up. A sample program which allows a Smalltalk user to manage a VVSet<Type> collection can be found in the VERSANT filein/demos directory.

You can browse a database using VMetaType class.

You can browse through the class definitions in a database by sending the message #metaInfoFor: to the Smalltalk/VERSANT VMetaType class. You will receive a VMetaType instance that describes the corresponding database schema without your having to import the corresponding class. This is particularly useful if you need to view schemas defined from C++/VERSANT which are not intended for object sharing.

You can store transient, runtime information.

You can store transient, runtime information or relationships in attributes defined as having the #VSI_ST_NIL storage transform.

Attribute names added by C++/VERSANT, such as __what and __vptr also have a #VSI_ST_NIL storage transform, but you should not use them to store runtime information, because future VERSANT releases may suppress these attributes when classes are imported into Smalltalk/VERSANT.

If you want to use an attribute to store transient, runtime information, then create and reserve an attribute in the class when you define it in C++.

There are some restrictions on queries over imported classes.

You can perform queries over imported classes using the same Smalltalk/VERSANT methods that you would use for any other persistent class. However, there are some restrictions and differences in usage.

When you import a class, the data types defined for the class in its database are often mapped to corresponding Smalltalk/VERSANT data types (see the following section for information on how types are mapped.) For example, the database elemental type o_4b is mapped to the Smalltalk SmallInteger and LargeInteger classes. Also, importing will cause a flattening of structure as expected by Smalltalk.

•    When you perform a path query, you must use the actual database attribute names and types known to the database rather than the Smalltalk variable names and types associated with the class as it appears in your Image. To get actual database names corresponding to an instance variable name, you can send the following messaage to the class with the instance variable:

#databaseAttributeNameFor:

•    When you perform a normal query, you can use instance variable names as they appear in the class definitions in your Image. For normal queries, Smalltalk/VERSANT will do any necessary conversions between instance variable names and database schema attribute names.

•    If you are querying on an elemental data type, because of possible variants in the way Smalltalk can map values (to SmallInteger or LargeInteger), you must prefix the value key with a type indicator in a pair of angle brackets.

For example, if you are querying on an age attribute in a Person class, and the age attribute has the database type o_u1b, then you must specify the query string as age=<VSI_ST_U1B>55 rather than age=55.

The database types and their corresponding type indicators are:

o_u1b  <VSI_ST_U1B>
o_u2b  <VSI_ST_U2B>
o_2b  <VSI_ST_2B>
o_u4b  <VSI_ST_U4B>
o_4b  <VSI_ST_4B>

The default type indicator is <VSI_ST_4B>.

•    If you are querying on a date or time attribute defined with the C++/VERSANT VDate or VTime classes, you must also provide a type indicator.

Database types and their corresponding type indicators are:

VDate  <VSI_ST_VDATE>
VTime  <VSI_ST_VTIME>

For example, for an embedded VTime attribute, all the following predicate strings are both valid and equivalent to one another:

"Use actual seconds of the VTime attribute."
'time = 811836204'
"Use the Timestamp formatted string."
'time =  <VSI_ST_VTIME>''9/22/95 11:04pm'''
"Use term key substitution."
| timestamp pred |
timestamp := Timestamp readFrom: '9/22/95 11:04pm'.
p := VPredicate from: 'time = <VSI_ST_VTIME>:x'.
p valuesAt: #(#x) put: (Array with: timestamp).

 

Storage Transform Concepts

When an object is read from a database into a Smalltalk Image, attribute values are converted to native Smalltalk objects. When an object in an Image is written to a database, these Smalltalk objects are converted back to values of their database domain type.

Due to language differences, the mapping between a database data type and Smalltalk type may be supported only within a given range.

For example, suppose that a database contains a class with an attribute of data type o_u1b (a one-byte, unsigned integer). When an object of that class is read from a database, the data type o_u1b will be mapped to the Smalltalk class SmallInteger. When that object is written back to a database, VERSANT will attempt to convert the value of type SmallInteger back to type o_u1b. This can cause problems, because a SmallInteger can contain a larger value than o_u1b. Similarly, a Smalltalk Character can have value from 0 to the largest SmallInteger, while the corresponding database data type char can only assum the values between -128 and 127.

In general, you must take responsibility for choosing the right C++ data type when creating classes whose instances will be used by both C++ and Smalltalk applications.

The following table lists all the type mapping supported in the current Smalltalk/VERSANT interface. The first column of the table list the data type that you might be using in your C++/VERSANT application when define the persistent classes. The second column lists the data type that the corresponding first column would be converted to. The third column is the name of the class that the corresponding database data type would be mapped to. The fourth column gives the corresponding storage transform symbol. For basic elemental data types, the supported data range is also listed.

 

Storage Transforms for Elemental Data Types

C++

Database

Smalltalk

Transform

Allowable

o_1b

char

Character

#VSI_ST_CHAR

0 to 127

char

char

Character

#VSI_ST_CHAR

0 to 127

signed char

char

Character

#VSI_ST_CHAR

0 to 127

o_bool

o_u1b

SmallInteger

#VSI_ST_U1B

0 to 255

o_u1b

o_u1b

SmallInteger

#VSI_ST_U1B

0 to 255

unsigned

o_u1b

SmallInteger

#VSI_ST_U1B

0 to 255

o_2b

o_2b

SmallInteger

#VSI_ST_2B

-32768 to +32767

short

o_2b

SmallInteger

#VSI_ST_2B

-32768 to +32767

signed

o_2b

SmallInteger

#VSI_ST_2B

-32768 to +32767

o_u2b

o_u2b

SmallInteger

#VSI_ST_U2B

0 to 65535

unsigned short

o_u2b

SmallInteger

#VSI_ST_U2B

0 to 65535

o_4b

o_4b

SmallInteger or LargeInteger

#VSI_ST_4B

-2147483648 to +2147483647

int

o_4b

SmallInteger or LargeInteger

#VSI_ST_4B

-2147483648 to +2147483647

long

o_4b

SmallInteger or LargeInteger

#VSI_ST_4B

-2147483648 to +2147483647

signed int

o_4b

SmallInteger or LargeInteger

#VSI_ST_4B

-2147483648 to +2147483647

signed long

o_4b

SmallInteger or LargeInteger

#VSI_ST_4B

-2147483648 to +2147483647

o_u4b

o_u4b

SmallInteger or LargeInteger

#VSI_ST_U4B

0 to +4294967295

unsigned

o_u4b

SmallInteger or LargeInteger

#VSI_ST_U4B

0 to +4294967295

unsigned int

o_u4b

SmallInteger or LargeInteger

#VSI_ST_U4B

0 to +4294967295

unsigned long

o_u4b

SmallInteger or LargeInteger

#VSI_ST_U4B

0 to +4294967295

o_float

o_float

Float

#VSI_ST_FLOAT

-10**38 to -+10**38, 6 digits of accuracy

float

o_float

Float

#VSI_ST_FLOAT

-10**38 to -+10**38, 6 digits of accuracy

o_double

o_double

Double

#VSI_ST_DOUBLE

-10**307 to +10**307, 14 digits of accuracy

double

o_double

Double

#VSI_ST_DOUBLE

-10**307 to +10**307, 14 digits of accuracy

type*

o_ptr

UndefinedObject

#VSI_ST_NIL

nil

Link<Type>

Link<Type>

Imported Class

#VSI_ST_OBJECT

 

o_object

o_object

Imported Class

#VSI_ST_OBJECT

 

 
 

Storage Transforms for Special Embedded Types

C++

Database

Smalltalk

Transform

VDate VDate Date #VSI_ST_VDATE
VTime VTime Timestamp #VSI_ST_VTIME
 

VDate and VTime are C++/VERSANT classes used for attributes. When brought into Smalltalk, attributes are instantiated as instances of Date and Timestamp.

These two embedded types are handled specially, because they are widely used in C++/VERSANT applications. Mapping these types to the Smalltalk Date and Timestamp class allows objects to share the behavior of the two existing Smalltalk classes. Also, it eliminates the need to duplicate the effort in providing methods for manipulating related attributes in each application specific imported class.

Methods are available to do date and timestamp value conversion between Date to VDate and and between Timestamp and VTime.

 

Storage Transforms for Fixed Array Types

C++

Database

Smalltalk

Transform

o_1b[n]

char[n]

Array of Character

#VSI_ST_CHAR_ARRAY

char[n]

char[n]

Array of Character

#VSI_ST_CHAR_ARRAY

signed char[n]

char[n]

Array of Character

#VSI_ST_CHAR_ARRAY

o_u1b[n]

o_u1b[n]

ByteArray

#VSI_ST_U1B_ARRAY

unsigned char[n]

o_u1b[n]

ByteArray

#VSI_ST_U1B_ARRAY

o_2b[n]

o_2b[n]

Array of SmallInteger

#VSI_ST_2B_ARRAY

short[n]

o_2b[n]

Array of SmallInteger

#VSI_ST_2B_ARRAY

signed short[n]

o_2b[n]

Array of SmallInteger

#VSI_ST_2B_ARRAY

o_u2b[n]

o_u2b[n]

Array of SmallInteger

#VSI_ST_U2B_ARRAY

unsigned short[n]

o_u2b[n]

Array of SmallInteger

#VSI_ST_U2B_ARRAY

o_4b[n]

o_4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_4B_ARRAY

int[n]

o_4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_4B_ARRAY

long[n]

o_4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_4B_ARRAY

signed int[n]

o_4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_4B_ARRAY

signed long[n]

o_4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_4B_ARRAY

o_u4b[n]

o_u4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_U4B_ARRAY

unsigned[n]

o_u4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_U4B_ARRAY

unsigned int[n]

o_u4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_U4B_ARRAY

unsigned long[n]

o_u4b[n]

Array of SmallInteger or Array of LargeInteger

#VSI_ST_U4B_ARRAY

o_float[n]

o_float[n]

Array of Float

#VSI_ST_FLOAT_ARRAY

float[n]

o_float[n]

Array of Float

#VSI_ST_FLOAT_ARRAY

o_double[n]

o_double[n]

Array of Double

#VSI_ST_DOUBLE_ARRAY

double[n]

o_double[n]

Array of Double

#VSI_ST_DOUBLE_ARRAY

type *[n]

o_ptr[n]

Array of nil

#VSI_ST_NIL_ARRAY

Link<Type>[n]

Link<Type> [n]

Array of Imported Class

#VSI_ST_OBJECT_ARRAY

o_object[n]

o_object[n]

Array of Imported Class

#VSI_ST_OBJECT_ARRAY

 
 

Storage Transforms for Vstr Types

C++

Database

Smalltalk

Transform

Vstr<o_1b>

Vstr<char>

ByteString

#VSI_ST_CHAR_VSTR

Vstr<char>

Vstr<char>

ByteString

#VSI_ST_CHAR_VSTR

PString

Vstr<char>

ByteString

#VSI_ST_CHAR_VSTR

VString

Vstr<char>

ByteString

#VSI_ST_CHAR_VSTR

Vstr<o_u1b>

Vstr<o_u1b>

ByteArray

#VSI_ST_U1B_VSTR

Vstr<unsigned char>

Vstr<o_u1b>

ByteArray

#VSI_ST_U1B_VSTR

Vstr<o_2b>

Vstr<o_2b>

OrderedCollection of SmallInteger

#VSI_ST_2B_VSTR

Vstr<short>

Vstr<o_2b>

OrderedCollection of SmallInteger

#VSI_ST_2B_VSTR

Vstr<signed short>

Vstr<o_2b>

OrderedCollection of SmallInteger

#VSI_ST_2B_VSTR

Vstr<o_u2b>

Vstr<o_u2b>

OrderedCollection of SmallInteger

#VSI_ST_U2B_VSTR

Vstr<unsigned short>

Vstr<o_u2b>

OrderedCollection of SmallInteger

#VSI_ST_U2B_VSTR

Vstr<o_4b>

Vstr<o_4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_4B_VSTR

Vstr<int>

Vstr<o_4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_4B_VSTR

Vstr<long>

Vstr<o_4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_4B_VSTR

Vstr<signed int>

Vstr<o_4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_4B_VSTR

Vstr<signed long>

Vstr<o_4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_4B_VSTR

Vstr<o_u4b>

Vstr<o_u4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_U4B_VSTR

Vstr<unsigned>

Vstr<o_u4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_U4B_VSTR

Vstr<unsigned int>

Vstr<o_u4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_U4B_VSTR

Vstr<unsigned long>

Vstr<o_u4b>

OrderedCollection of SmallInteger or LargeInteger

#VSI_ST_U4B_VSTR

Vstr<o_float>

Vstr<o_float>

OrderedCollection of Float

#VSI_ST_FLOAT_VSTR

Vstr<float>

Vstr<o_float>

OrderedCollection of Float

#VSI_ST_FLOAT_VSTR

Vstr<o_double>

Vstr<o_double>

OrderedCollection of Double

#VSI_ST_DOUBLE_VSTR

Vstr<double>

Vstr<o_double>

OrderedCollection of Double

#VSI_ST_DOUBLE_VSTR

LinkVstr<Type>

LinkVstr<Type>

OrderedCollection of Imported Class

#VSI_ST_OBJECT_VSTR

 
 

Object Sharing Methods

Following are methods useful when sharing objects.

ODBInterface

importSchema: — Import a class definition.

PObject

fileInSourceFromDB: — Answer file-out source text.

loadSourceFromDB: — Load file-out source.

storeSourceToDB: — Store file-out source.

vElementalHash — Answer an integer value that can be used as a hash index in a collection.

PVirtual

vElementalCombinedHash: — Answer a combined hash value.

VMetaType

allCXXDefinedSchemsFromDB: — Answer descriptions of all database classes.

attributes — Answer descriptions of class attributes.

metaInfoFor: — Answer a description of a database class.

name — Answer the name of a database class.

supers — Answer descriptions of super classes.

VAttribute

domain — Answer the database class name.

name — Answer the attribute name.

repeatFactor — Answer the attribute repeat factor.

storageTransform — Answer the derived storage transformation symbol for the attribute.

type — Answer the attribute type.

ByteString

vElementalHash — Answer a hash value for a ByteString object.

Character

vElementalHash — Answer a hash value for a Character object.

Double

vElementalHash — Answer a hash value for a Double object.

Float

vElementalHash — Answer a hash value for a Float object.

Integer

vElementalHash — Answer a hash value for an Integer object.

 

 

 


This online documentation is confidential and proprietary to Versant Corporation and is licensed to you, or to your organization, pursuant to the terms of an agreement between you and Versant that restricts the use of this documentation. Please refer to the agreement for more information or call Versant at 510-789-1500 with any questions.