Generating Proxy Classes
Proxy classes are generated by the Caché C++ Class Generator (see Using the C++ Generator), which reads the definition of a Caché class and uses the information to generate a corresponding C++ class. The generated class provides remote access to an instance of a Caché object from within a C++ application.
Introduction
The C++ Generator produces C++ proxy classes that have the same inheritance hierarchy as the corresponding Caché classes. The type of a class (such as persistent or serial) determines its corresponding C++ superclass. For example, persistent classes have corresponding C++ classes derived from the C++ Persistent_t class included in the Caché C++ library. In case of multiple inheritance, a class becomes a subclass of the first superclass in Caché, and all methods and properties from other direct superclasses are generated as members of the proxy class.
The Caché C++ class library includes C++ versions of a number of the classes within the Caché class library, including %PersistentOpens in a new tab, %RegisteredObjectOpens in a new tab, %SerialObjectOpens in a new tab, the various Caché collection classes, and C++ versions of the various data type classes. In addition, the library contains the various classes used within a C++ application to manage the communication with the Caché server.
The C++ binding doesn't check at runtime to see whether metadata has changed since code was generated. In particular, it doesn't check whether the application is connecting to the same namespace as at code generation time, and doesn't check whether the classes are defined in the runtime namespace. If they aren't, it will go ahead and insert data anyway, but this data won't be accessible via SQL or via the object interface.
Standard Proxy Class Methods
In addition to any methods defined by a Caché class, all C++ proxy classes inherit a set of methods from the standard Caché C++ library classes Persistent_t (for persistent classes) or Registered_t (for serial classes).
The C++ Generator also adds a set of static create and open methods to the generated classes. To protect the C++ classes from incorrect usage, the proxy class constructors are made private. The only way to instantiate a generated class T is to call one of the static methods T::create_new(), T::open(), or T::openid(), each of which returns a d_ref<T> object (see Using Proxy Objects). These methods are generated only if the corresponding Caché methods exist for a given class.
The static methods are defined as follows (where My_Class is the name of the proxy class):
-
create_new() — Creates an object on the server by calling the %New method.
static d_ref<My_Class> create_new( Database* db, const_str_t init_val = 0, // const_str_t is a typedef of const wchar_t* Db_err* err = 0)
-
open() — Generated for persistent classes only. Calls %Open on the server to open an object using its complete Object ID.
static d_ref<My_Class> open( Database* db, const d_binary& ident, int concurrency = -1, int timeout = -1, Db_err* err = 0)
-
openid() — Calls %Openid on the server to open an object using its extent-specific ID value.
static d_ref<My_Class> openid( Database* db, const const_str_t ident, // const_str_t is a typedef of const wchar_t* int concurrency = -1, int timeout = -1, Db_err* err = 0)
Implementing Proxy Methods
C++ instance methods are generated for Caché instance methods and C++ static methods are generated for Caché class methods. When called on the client, a C++ method invokes the actual method implementation on the Caché server. If a method signature includes arguments with default values, Caché uses the same default values within the generated C++ method. For example, suppose you define a simple Caché class with one method:
Class MyApp.Simple Extends %RegisteredObject {
Method LookupName(id As %String) As %String {
// lookup a name using embedded SQL
Set name = ""
&sql(SELECT Name INTO :name FROM Person WHERE ID = :id)
Quit name
}
}
The resulting C++ class header would look similar to the following:
class MyApp_Simple : public Persistent_t {
friend d_ref<MyApp_Simple>;
public:
// code
virtual d_string LookupName(d_string id);
}
When a method is invoked from C++, the C++ client first synchronizes the server object cache, then invokes the method on the Caché server, and finally, returns the resulting value (if any). If any method arguments are specified as call-by-reference then their value is updated as well.
Implementing Proxy Properties
Properties in C++ proxy classes are accessed through a pair of accessor methods. Each property has a corresponding get<Property>() method to get its value and a set<Property>() method to set its value.
The values for literal properties (such as strings or integers) are represented using the appropriate C++ data type classes provided with the Caché C++ class library (such as d_string or d_int).
The values for object-valued properties are represented using the d_ref template class (see Using Proxy Objects).
For example, suppose you have defined a persistent class within Caché containing two properties, one literal and the other object-valued:
Class MyApp.Student Extends %Persistent {
// Student's name
Property Name As %String;
// Reference to a school object
Property School As School;
}
The C++ representation of MyApp.Student contains get and set accessor methods for both the Name and School properties. In addition, it provides accessors for the object Id for the referenced School object.
For example, in the Caché sample class, Sample.Person, the DOB property is defined as follows:
Property DOB As %Date(POPSPEC = "Date()");
The POPSPEC content is for populating the class with sample data and would not appear in an actual application. The C++ accessor methods for Sample.PersonOpens in a new tab are:
virtual d_date getDOB() const;
virtual void setDOB(const d_date&);
When a C++ object is instantiated within C++, it fetches a copy of its property values from the Caché server and copies them into a local client-side cache. Subsequent access to the object's property values are made against this cache, reducing the number of messages sent to and from the server. Caché automatically manages this local cache and ensures that it is synchronized with the corresponding object state on the Caché server.
Property values for which you have defined a Get or Set method within your Caché class definition (to create a property whose value depends on other properties for example) are not stored within the local cache. Instead when you access such properties the corresponding accessor method is invoked on the Caché server. As this can entail higher network traffic, you should exercise care when using such properties within a client/server environment.
Naming Conventions
A generated C++ identifier, such as the name of a class, method, or variable, is usually the same as that of the corresponding Caché identifier. This section describes the exceptions to that rule.
It is important to remember that, unlike ObjectScript, C++ identifiers must contain only characters A-Z, a-z, 1–9, and "_" (underscore). If a Caché identifier contains characters that are not permitted in C++, those characters will be replaced by underscores. If the Caché identifier consists of high-order Unicode characters, this may result in a C++ identifier that contains nothing but underscores. Alternate class and package names can be defined in Caché, as described below.
-
Class and Package Names
Because C++ does not support packages, the Caché package name for a class is added to the start of the C++ class name with the "_" character replacing the "." character. The class name itself is unchanged.
If your Caché code defines both the package client name and the ClientName parameter of a class, the Caché C++ Generator will use these parameters instead of the class and package names. In Studio, you can set the package name by right-clicking on the package name, choosing Package Information in the context menu, and then entering the value of the Client Name field. The ClientName parameter of a class can be defined using the Class Inspector.
-
Method Names
Typically, method names are mapped directly to C++ methods, without changes. Exceptions are:
-
If the method name starts with "%", this is replaced by "sys_".
-
If the method name is a C++ reserved word, "_" is prepended to the name.
-
-
Property Names
On the server you can refer directly to a Caché object's properties. To encapsulate property behavior for C++, two C++ accessor methods are generated for each Caché property. For a given property Prop, the accessor methods are getProp() and setProp(). If the property name starts with "%", it is replaced by "sys_". Hence, the accessor methods of a Color property would be getColor() and setColor(). The accessor methods of a %Concurrency property would be get_sys_Concurrency() and set_sys_Concurrency().
-
Formal Variable Names
If a variable within a method formal list starts with "%", it is replaced by "_". If the name is a reserved word, "_" is prepended to the name.
For details on Caché Basic and ObjectScript naming conventions, see Variables in Using Caché ObjectScript, Naming Conventions in Using Caché Objects, Identifiers and Variables in Using Caché Basic, and Rules and Guidelines for Identifiers in the Caché Programming Orientation Guide.
Using the C++ Generator
The Caché C++ Generator is a program that generates a C++ class and header file from a Caché class definition. It is available either as a command line program, or as an option in Studio. If Caché is installed with level 3 ("locked down") security, %Service_Bindings must be enabled in order to run the Generator.
To access the Generator from Studio, select Tools > Generate C++ projection from the main menu. This option does not allow you to generate projections for the Light C++ Binding, which must use the command line program with the -lc parameter.
The command line program, cpp_generator.exe, is installed in the <cachesys>\dev\cpp\lib directory, which must be in your Path. (See Default Caché Installation Directory in the Caché Installation Guide for the location of <cachesys> on your system).
The syntax for the program is:
cpp_generator
-conn <conn>
-user <user>
-pswd <password>
-path <path>
[-class <class>] | [-class-list <filename>]
[-lc]
[-help]
For example:
cpp_generator
-conn "localhost[1972]:SAMPLES"
-user "MyUserName"
-pswd "MyPassword"
-path "./cppfiles"
-class "Sample.Person"
-lc
-conn |
A connection string with the format <host>[<port>]:<namespace>. For example: -conn "localhost[1972]:SAMPLES" |
-user |
A string specifying the username. |
-pswd |
A string specifying the password. |
-class or -class-list |
Either a classname or the name of a file containing a list of classnames. The -class <class> option specifies a Caché server classname. For example: -class "Sample.Person" The -class-list <filename> option specifies the name of a file containing Caché server class name strings (and nothing else), one per line. For example: -class-list "\Mydir\classlist.txt" where classlist.txt contains the following lines: Sample.Person Sample.Company |
-path |
A string specifying the directory in which the generated C++ class is to be placed. |
-lc | Optional. If the -lc switch is used, the generator will produce Light C++ Binding classes. |
-help | Optional. Displays the list of C++ Generator parameters. |
The C++ Generator will automatically generate code for any other classes required to implement the specified class. For example, if you specify -class "Sample.Employee", code will also be generated for the Sample.PersonOpens in a new tab and Sample.AddressOpens in a new tab classes, because Sample.EmployeeOpens in a new tab is derived from Sample.PersonOpens in a new tab, and Sample.PersonOpens in a new tab has properties of type Sample.AddressOpens in a new tab.