Problem creating dictionary using pure C headers

Short description of a problem:
I need to implement IBM DB2 connectivity using provided CLI (Call Level Interface) interface (.so lib and lots of headers - there is no way to change headers and lib due to licence limitations). I’ve written few classes that use CLI types and functions, and then built another .so lib. The problem is that CLI is pure C, lots of structs, typedefs, ifdefs etc, in general these headers are quite complex. In addition lets say the “main” header includes another and so on. When I try to build dictionary using rootcint all of these C stuff is being converted to C++ (strange namespaces, classes, etc.). Unfortunately converted code is one big error. There is not a chance to correct the code manually, as the dict file has over 5000 lines and fixing one error generates at least three new ones.

The question is how to make rootcint leave included IBMs headers alone and create dictionary describing my classes only. How to prepare my classes and headers and how to prepare LinkDefs.

Or maybe this is just pointless… :slight_smile:

I’d be very grateful for any support

Michal Tomaszewski, student
Faculty of Physics
Warsaw University of Technology

Hi,

you can use#ifndef __CINT__ #include "CLI.h" #endif to hide stuff from CINT (and thus rootcint).

If one of your classes has a member of a type defined in these headers its type will of course not be known to (root)CINT, because you’re hiding the header defining it. So for i/o to work, you need to re-define it to something “comparable” in size.

Example: lets assume the header file CLI.h of the CLI library looks something like this:

struct CLI_B{ struct CLI_B* ptr; }; struct CLI { unsigned short a; struct CLI_B* b; long long c; };
and your code:[code]
#ifndef CINT
#include “CLI.h”
#endif

#ifdef CINT
/* dummy definition of CLI, so CINT knows what
and where its members are, and ROOT can do
i/o of MyClass*/

struct CLI {
unsigned short a;
/* we don’t have the dictionary for CLI_B anyway,
and the size of a pointer is the same, no matter
what it points to /
void
b;
long long c;
};
#endif

class MyClass: public TObject {
private:
CLI fCLI;
public:

};[/code]

Cheers, Axel.

Thanks for such a quick RE.
Unfortunately in my opinion it can’t be done that easy.
Not only types defined in CLI headers were used but also some funcions mantainig tasks like allocating so called handles, connecting to or disconnectng from DB, etc. Moreover I don’t exactly understand how hiding IBMs headers and creating dummy structs so as to maintain proper mem allocation can prevent rootcint from converting them to classes while creating dictionary.

I’ll paste some real code so it’ll be easier to pointout the problem.
here goes my header:

class Tdb2: public TObject
{
      private:
              SQLRETURN cliRC; /*CLI return code*/
              int rc; /*return code*/
              SQLHANDLE henv; /*CLI environment handle*/
              SQLHANDLE hdbc; /*CLI connection handle*/ 
              char dbAlias[SQL_MAX_DSN_LENGTH + 1];
              char user[MAX_UID_LENGTH + 1];
              char pswd[MAX_PWD_LENGTH + 1];
              int DbDriverConnect(SQLHANDLE, char *, char*, char*); /*assume additional connection parameters*/ 
      public:
             Tdb2(); /*default constructor for test purpose only*/
             Tdb2(char * /*dbAlias*/, char * /*user*/, char * /*pswd*/);
             ~Tdb2(); /*destructor mainly for freing CLI handles*/
             
};

and now some defs:

#include <string>
#include <stdio>
#include "Tdb2.h"
#include "/home/db2inst1/sqllib/include/sqlcli1.h" //<-MUST REMAIN AS IS

Tdb2::Tdb2() /*default constructor definition; for test purpose only*/
{
            dbAlias = "SAMPLE"; /*set db info*/
            user = "";
            pswd = "";
            
            /*allocate a CLI environment handle*/
            cliRC = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
            if(cliRC != SQL_SUCCESS) /*whether alloc is successful*/
            {
                     std::cout<<"\n CLI error in SQLAllocHandle()! Unable to alloc environment handle!\n";
                     exit(-1);
            }
            /*set env handle attribute to run as ODBC 3.0 compliant application*/
            cliRC = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);      
            SQLCHAR connStr[255]; 
            /* allocate a database connection handle */
            cliRC = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
            if(cliRC != SQL_SUCCESS) /*whether alloc is successful*/
            {
                     std::cout<<"\n CLI error in SQLAllocHandle()! Unable to alloc connection handle!\n";
                     exit(-1);
            }
            /*prepare connection string*/
            sprintf((char *)connStr, "DSN=%s; UID=%s; PWD=%s; AUTOCOMMIT=0; CONNECTTYPE=1;", dbAlias, user, pswd);
            /*connect to database*/
            cliRC = SQLDriverConnect(hdbc, (SQLHWND)NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
            if(cliRC != SQL_SUCCESS) /*whether connect is successful*/
            {
                     std::cout<<"\n CLI error in SQLDriverConnect()! Unable to connect to database " << dbAlias << std::cout;
                     exit(-1);
            }            
            std::cout << "\n Connected to database " << dbAlias << std::endl;
            /*disconnect from database*/
            cliRC = SQLDisconnect(hdbc);
            if(cliRC != SQL_SUCCESS) /*whether disconnect is successful*/
            {
                     std::cout<<"\n CLI error in SQLDisconnect()! Unable to disconnect from database " << dbAlias << std::cout;
                     exit(-1);
            }
            std::cout << "\nDisconnected form database " << dbAlias << std::endl;
}
Tdb2::Tdb2(char dbAlias[], char user[], char pswd[])
{
}
Tdb2::~Tdb2()
{
             /*free the connection handle*/
             cliRC = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
             /* free the environment handle */
             cliRC = SQLFreeHandle(SQL_HANDLE_ENV, henv);            
}

As you can see #include “/home/db2inst1/sqllib/include/sqlcli1.h” provides types for members and funcs used in methods/constructors. sqlcli1.h includes other .h files (sql.h, sqlutil.h and about four more) and MUST remain as is.

Functions declared in sqlcli1.h and other are defined in (already built) libdb2.so provided by IBM, so there is no way to modify anything.
IMHO there is not a chance to get CLI rewritten in C++ (no deal for IBM :slight_smile:)

The goal is to force CINT understand this C/C++ mixture and then call Tdb2 *blahblah = new Tdb2() in ROOT and be happy(=to get positive grade) :slight_smile:

Hi,

well, you just do as I suggested, and re-define SQLHANDLE etc properly for (root)CINT. rootcint just cares about the header - in your implementation you can do whatever you want. And you can of course re-define a CINT-dummy implementation of e.g. SQLHANDLE as a struct, no problem. In your linkdef just put a #pragma link C++ class Tdb2+; as you probably don’t need any of the structs etc from the IBM headers.

Cheers, Axel.

Thanks Axel!
I’ve managed to implement db2 interface by following your hints, although it wasn’t that simple.