Subsections


Using the C++ Binding

We are going to introduce now the C++ binding through the same schema and examples as previously.

There are two ways to use the C++ binding:

  1. using the generic C++ binding
  2. using both the generic C++ binding and the specific Person C++ code generated from the ODL schema
We will explain here only the second way, as it is far more simple and pratical than the first one. For more information on the generic C++ binding, please refer to the C++ binding manual.

Writing a C++ program that can create, retrieve, modify and delete person instances that are stored in an EYEDB database involves the following steps:
  1. generates the specific Person binding using the eyedbodl tool
  2. write the C++ client program
  3. compile the generated binding and the client program
This example is located in the examples/GettingStarted subdirectory.


Generating the specific C++ binding

To generate the specific C++ binding, run the eyedbodl tool as follow:

% eyedbodl --gencode=C++ --package=person schema.odl
The -package option is mandatory: you may give any name you want, this name will be used as the prefix for generated files names. Without the -package option, the prefix used will be the name of the ODL file without its extension.

eyedbodl generates a few files, all prefixed by person, the most important being person.h and person.cc.

If you have a look to the file person.h, you will notice that the following classes have been generated:
  1. the class person
  2. the class personDatabase
  3. the class Root
  4. the class Address
  5. the class Person
  6. the class Employee
The first class, person, is the package class:
class person {
 public:
  static void init();
  static void release();
  static eyedb::Status updateSchema(eyedb::Database *db);
  static eyedb::Status updateSchema(eyedb::Schema *m);
};
it is used to perform package initialization and schema update. Before any use of the person package, you need to call person::init.

The second class, personDatabase is used to open, close and manipulate objects within a database containing the person schema.

The open method has two purposes: the first one is to open the database, as the standard eyedb::Database will do; the second one is to check that the database schema is consistant with the generated runtime schema. Although it is possible to use the standard Database class to open a database containing the person schema, it is strongly recommended to use the personDatabase class.

The third class, Root, is the root class for all the generated classes. This class is useful to perform safe down-casting during object loading.

The three last classes, Address, Person and Employee are generated from the person.odl class specifications: for each attribute in the person.odl, a set of get and set methods is generated.

For instance, for the firstname attribute, the following methods are generated:
  eyedb::Status setFirstname(const std::string &);
  std::string getFirstname(eyedb::Bool *isnull = 0, eyedb::Status * = 0) const;
  eyedb::Status setFirstname(unsigned int a0, char);
  char getFirstname(unsigned int a0, eyedb::Bool *isnull = 0, eyedb::Status * = 0)  const;
The two first methods manipulate the firstname attribute as a string while the two last ones manipulate each character within this string.

There are two set methods and two get methods.


A minimal client program

We are now going to write a minimal client program which will perform the following operations:
  1. initialize the EYEDB package and the person package
  2. open a connection with the EYEDB server
  3. open a database
  4. perform error management
  5. release the EYEDB package and the person package
Here is the code for this minimal client:
#include "person.h"

int
main(int argc, char *argv[])
{
  eyedb::init(argc, argv);      // initializes EyeDB package
  person::init();               // initializes person package

  eyedb::Exception::setMode(eyedb::Exception::ExceptionMode); // use exception mode

  try {
    eyedb::Connection conn;

    conn.open();                // opens the connection

    personDatabase db(argv[1]); // creates a database handle
    db.open(&conn, eyedb::Database::DBRW); // opens the database in read/write mode
  }

  catch(Exception &e) {      // catch any exception and print it
    e.print();
  }

  person::release();            // releases person package
  eyedb::release();             // releases EyeDB package

  return 0;
}
Note that statement Exception::setMode(...) is mandatory if you want to use the exception error policy.

To use this client, you must first compile it: eyedbodl has generated a makefile called Makefile.package which can be used as is or can help you to design your own makefile.

A template C++ file (template_package.cc) has also been generated, closed to the previous minimal client program, which can be compiled with the generated makefile.

Here is the generated Makefile.person ((«datadir» is the data directory, usually /usr/share):
#
# Makefile.person
# 
# person package
#
# Example of template Makefile that can help you to compile
# the generated C++ file and the template program
# Generated by eyedbodl at Sat Jan 28 17:53:48 2006
#

include <<datadir>>/eyedb/Makefile.eyedb

CXXFLAGS += $(EYEDB_CXXFLAGS) $(EYEDB_CPPFLAGS)
LDFLAGS  += ${EYEDB_LDFLAGS}
LDLIBS   += ${EYEDB_LDLIBS}

# if you use gcc
GCC_FLAGS = -Wl,-R$(EYEDB_LIBDIR)

# Example for compiling a client program:

client_program = template_person

$(client_program): person.o $(client_program).o
        $(CXX) $(LDFLAGS) $(GCC_FLAGS) -o $@ $^ $(LDLIBS)
Important note: you need a recent version of GNU make to use this makefile. This makefile does not work with the standard SUN make.

Once compiled, you can execute the program as follows:
% ./persontest foo
We are going now to add a function to manipulate Person instances:
  1. create a person named ``john wayne''
  2. create a person named ``mary poppins''
  3. mary them
  4. create 3 ``john wayne'' children named ``baby1'', ``baby2'' and ``baby3''
These operations are performed in the following function:
static void
create(eyedb::Database *db)
{
  db->transactionBegin(); // starts a new transaction

  Person *john = new Person(db);
  john->setFirstname("john");
  john->setLastname("wayne");
  john->setAge(32);
  john->getAddr()->setStreet("courcelles");
  john->getAddr()->setTown("Paris");

  Person *mary = new Person(db);
  mary->setFirstname("mary");
  mary->setLastname("poppins");
  mary->setAge(30);
  mary->getAddr()->setStreet("courcelles");
  mary->getAddr()->setTown("Paris");

  // mary them
  john->setSpouse(mary);

  // creates children
  for (int i = 0; i < 5; i++) {
    std::string name = std::string("baby") + str_convert(i+1);
    Person *child = new Person(db);
    child->setFirstname(name.c_str());
    child->setLastname(name.c_str());
    child->setAge(1+i);
    john->addToChildrenColl(child);
    child->release(); // release the allocated pointer
  }

  // store john and all its related instances within the database
  john->store(eyedb::FullRecurs);

  // release the allocated pointers
  mary->release();
  john->release();

  db->transactionCommit(); // commits the current transaction
}
A few remarks about this code: We are now going to query and display all the person instances.

Here is the corresponding code:
static void
read(eyedb::Database *db, const char *s)
{
  db->transactionBegin();

  eyedb::OQL q(db, "select Person.lastname ~ \"%s\"", s);

  eyedb::ObjectArray obj_arr;
  q.execute(obj_arr);

  for (int i = 0; i < obj_arr.getCount(); i++) {
    Person *p = Person_c(obj_arr[i]);
    if (p)
      printf("person = %s %s, age = %d\n", p->getFirstname(),
             p->getLastname(), p->getAge());
  }

  db->transactionCommit();
}
An OQL construct can be used within the C++ code using the OQL(Database *, const char *fmt, ...) constructor. For instance, in the above example, assuming s is equal to baby, the code:
  eyedb::OQL q(db, "select Person.lastname ~ \"%s\"", s);
will send the query select Person.lastname  "baby" to the OQL interpreter.

This interpreter will perform the query and returned all the found objects. The returned objects can be found using the OQL::execute method as follows:
  eyedb::ObjectArray obj_arr;
  q.execute(obj_arr);
The returned objects are of type eyedb::Object, so you cannot use the Person methods such as getFirstname(), getAge()... To use them, you need to perform a down-cast using the Person_c static function as follows:
  for (int i = 0; i < obj_arr.getCount(); i++) {
    Person *p = Person_c(obj_arr[i]);
    if (p) ...
If the object obj_arr[i] is not of type Person, the returned pointer will be null. It is why we make a test on the value of p. If p is not null, we can use all the Person methods as follows:
    printf("person = %s %s, age = %d\n", p->getFirstname(),
           p->getLastname(), p->getAge());

To have more information about the C++ binding, please refer to the EYEDB C++ binding manual.

EyeDB manual