Pro*C/C++ Precompiler Programmer's Guide Release 8.1.5 A68022-01 |
|
This chapter describes how you can use the Pro*C/C++ Precompiler to precompile your C++ embedded SQL application, and how Pro*C/C++ generates C++ compatible code.
Topics are:
To understand how Pro*C/C++ supports C++, you must understand the basic functional capabilities of Pro*C/C++. In particular, you must be aware of how Pro*C/C++ differs from Pro*C Version 1.
The basic capabilities of Pro*C/C++ are:
To support its C preprocessor capabilities and to enable host variables to be declared outside a special Declare Section, Pro*C/C++ incorporates a complete C parser. The Pro*C/C++ parser is a C parser; it cannot parse C++ code.
This means that for C++ support, you must be able to disable the C parser, or at least partially disable it. To disable the C parser, the Pro*C/C++ Precompiler includes command-line options to give you control over the extent of C parsing that Pro*C/C++ performs on your source code. The section "Precompiling for C++" fully describes these options.
Using C++ with Pro*C/C++ does not require any special preprocessing or special macro processors that are external to Pro*C/C++. There is no need to run a macro processor on the output of the precompiler to achieve C++ compatibility.
If you are a user of a release of Pro*C/C++ Precompiler before this one, and you did use macro processors on the precompiler output, you should be able to precompile your C++ applications using Pro*C/C++ with no changes to your code.
To control precompilation so that it accommodates C++, there are four considerations:
You must be able to specify what kind of code, C compatible code or C++ compatible code, the precompiler generates. Pro*C/C++ by default generates C code. C++ is not a perfect superset of C. Some changes are required in generated code so that it can be compiled by a C++ compiler.
For example, in addition to emitting your application code, the precompiler interposes calls to its runtime library, SQLLIB. The functions in SQLLIB are C functions. There is no special C++ version of SQLLIB. For this reason, if you want to compile the generated code using a C++ compiler, Pro*C/C++ must declare the functions called in SQLLIB as C functions.
For C output, the precompiler would generate a prototype such as
void sqlora(unsigned long *, void *);
But for C++ compatible code, the precompiler must generate
extern "C" {
void sqlora(unsigned long *, void *);
};
You control the kind of code Pro*C/C++ generates using the precompiler option CODE. There are three values for this option: CPP, KR_C, and ANSI_C. The differences between these options can be illustrated by considering how the declaration of the SQLLIB function sqlora differs among the three values for the CODE option:
void sqlora( /*_ unsigned long *, void * _*/); /* K&R C */
void sqlora(unsigned long *, void *); /* ANSI C */
extern "C" { /* CPP */
void sqlora(unsigned long *, void *);
};
When you specify CODE=CPP, the precompiler
See "CODE" for information about the KR_C and ANSI_C values for the CODE option.
You must be able to control the effect of the Pro*C/C++ C parser on your code. You do this by using the PARSE precompiler option, which controls how the precompiler's C parser treats your code.
The values and effects of the PARSE option are:
This option value is the default if the value of the CODE option is anything other than CPP. It is an error to specify PARSE=FULL when CODE=CPP.
To generate C++ compatible code, the PARSE option must be either NONE or PARTIAL. If PARSE=FULL, the C parser runs, and it does not understand C++ constructs in your code, such as classes.
Most C compilers expect a default extension of ".c" for their input files. Different C++ compilers, however, can expect different filename extensions. The CPP_SUFFIX option allows you to specify the filename extension that the precompiler generates. The value of this option is a string, without the quotes or the period. For example, CPP_SUFFIX=cc, or CPP_SUFFIX=C.
Pro*C/C++ searches for standard system header files, such as stdio.h, in standard locations that are platform specific. Pro*C/C++ does not search for header files with extensions such as hpp or h++. For example, on almost all UNIX systems, the file stdio.h has the full pathname /usr/include/stdio.h.
But a C++ compiler has its own version of stdio.h that is not in the standard system location. When you are precompiling for C++, you must use the SYS_INCLUDE precompiler option to specify the directory paths that Pro*C/C++ searches to look for system header files. For example:
SYS_INCLUDE=(/usr/lang/SC2.0.1/include,/usr/lang/SC2.1.1/include)
Use the INCLUDE precompiler option to specify the location of non-system header files. See "INCLUDE". The directories specified by the SYS_INCLUDE option are searched before directories specified by the INCLUDE option.
If PARSE=NONE, the values specified in SYS_INCLUDE and INCLUDE for system files are not relevant, since there is no need for Pro*C/C++ to include system header files. (You can, of course, still include Pro*C/C++-specific headers, such sqlca.h, using the EXEC SQL INCLUDE statement.)
This section includes three sample Pro*C/C++ programs that include C++ constructs. Each of these programs is available on-line, in your demo directory.
/* cppdemo1.pc * * Prompts the user for an employee number, then queries the * emp table for the employee's name, salary and commission. * Uses indicator variables (in an indicator struct) to * determine if the commission is NULL. */ #include <iostream.h> #include <stdio.h> #include <string.h> // Parse=partial by default when code=cpp, // so preprocessor directives are recognized and parsed fully. #define UNAME_LEN 20 #define PWD_LEN 40 // Declare section is required when CODE=CPP and/or // PARSE={PARTIAL|NONE} EXEC SQL BEGIN DECLARE SECTION; VARCHAR username[UNAME_LEN]; // VARCHAR is an ORACLE pseudotype varchar password[PWD_LEN]; // can be in lower case also // Define a host structure for the output values // of a SELECT statement struct empdat { VARCHAR emp_name[UNAME_LEN]; float salary; float commission; } emprec; // Define an indicator struct to correspond to the // host output struct struct empind { short emp_name_ind; short sal_ind; short comm_ind; } emprec_ind; // Input host variables int emp_number; int total_queried; EXEC SQL END DECLARE SECTION; // Define a C++ class object to match the desired // struct from the above declare section. class emp { char ename[UNAME_LEN]; float salary; float commission; public: // Define a constructor for this C++ object that // takes ordinary C objects. emp(empdat&, empind&); friend ostream& operator<<(ostream&, emp&); }; emp::emp(empdat& dat, empind& ind) { strncpy(ename, (char *)dat.emp_name.arr, dat.emp_name.len); ename[dat.emp_name.len] = '\0'; this->salary = dat.salary; this->commission = (ind.comm_ind < 0) ? 0 : dat.commission; } ostream& operator<<(ostream& s, emp& e) { return s << e.ename << " earns " << e.salary << " plus " << e.commission << " commission." << endl << endl; } // Include the SQL Communications Area // You can use #include or EXEC SQL INCLUDE #include <sqlca.h> // Declare error handling function void sql_error(char *msg); main() { char temp_char[32]; // Register sql_error() as the error handler EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error:"); // Connect to ORACLE. Program calls sql_error() // if an error occurs // when connecting to the default database. // Note the (char *) cast when // copying into the VARCHAR array buffer. username.len = strlen(strcpy((char *)username.arr, "SCOTT")); password.len = strlen(strcpy((char *)password.arr, "TIGER")); EXEC SQL CONNECT :username IDENTIFIED BY :password; // Here again, note the (char *) cast when using VARCHARs cout << "\nConnected to ORACLE as user: " << (char *)username.arr << endl << endl; // Loop, selecting individual employee's results total_queried = 0; while (1) { emp_number = 0; printf("Enter employee number (0 to quit): "); gets(temp_char); emp_number = atoi(temp_char); if (emp_number == 0) break; // Branch to the notfound label when the // 1403 ("No data found") condition occurs EXEC SQL WHENEVER NOT FOUND GOTO notfound; EXEC SQL SELECT ename, sal, comm INTO :emprec INDICATOR :emprec_ind // You can also use // C++ style FROM EMP // Comments in SQL statemtents. WHERE EMPNO = :emp_number; { // Basic idea is to pass C objects to // C++ constructors thus // creating equivalent C++ objects used in the // usual C++ way emp e(emprec, emprec_ind); cout << e; } total_queried++; continue; notfound: cout << "Not a valid employee number - try again." << endl << endl; } // end while(1) cout << endl << "Total rows returned was " << total_queried << endl; cout << "Have a nice day!" << endl << endl; // Disconnect from ORACLE EXEC SQL COMMIT WORK RELEASE; exit(0); } void sql_error(char *msg) { EXEC SQL WHENEVER SQLERROR CONTINUE; cout << endl << msg << endl; cout << sqlca.sqlerrm.sqlerrmc << endl; EXEC SQL ROLLBACK RELEASE; exit(1); }
The next application is a simple modular example. First, execute the following SQL script, cppdemo2.sql
, in SQL*Plus:
Rem This is the SQL script that accompanies the cppdemo2 C++ Demo Rem Program. Run this prior to Precompiling the empclass.pc file. / CONNECT SCOTT/TIGER / CREATE OR REPLACE VIEW emp_view AS SELECT ename, empno FROM EMP / CREATE OR REPLACE PACKAGE emp_package AS TYPE emp_cursor_type IS REF CURSOR RETURN emp_view%ROWTYPE; PROCEDURE open_cursor(curs IN OUT emp_cursor_type); END emp_package; / CREATE OR REPLACE PACKAGE BODY emp_package AS PROCEDURE open_cursor(curs IN OUT emp_cursor_type) IS BEGIN OPEN curs FOR SELECT ename, empno FROM emp_view ORDER BY ename ASC; END; END emp_package; / EXIT /
The header file empclass.h
defines the class emp
:
// This class definition may be included in a Pro*C/C++ application // program using the EXEC SQL INCLUDE directive only. Because it // contains EXEC SQL syntax, it may not be included using a #include // directive. Any program that includes this header must be // precompiled with the CODE=CPP option. This emp class definition // is used when building the cppdemo2 C++ Demo Program. class emp { public: emp(); // Constructor: ALLOCATE Cursor Variable ~emp(); // Desctructor: FREE Cursor Variable void open(); // Open Cursor void fetch() throw (int); // Fetch (throw NOT FOUND condition) void close(); // Close Cursor void emp_error(); // Error Handler EXEC SQL BEGIN DECLARE SECTION; // When included via EXEC SQL INCLUDE, class variables have // global scope and are thus basically treated as ordinary // global variables by Pro*C/C++ during precompilation. char ename[10]; int empno; EXEC SQL END DECLARE SECTION; private: EXEC SQL BEGIN DECLARE SECTION; // Pro*C/C++ treats this as a simple global variable also. SQL_CURSOR emp_cursor; EXEC SQL END DECLARE SECTION; };
The code in empclass.pc
contains the emp
methods:
#include <stdio.h> #include <stdlib.h> // This example uses a single (global) SQLCA that is shared by the // emp class implementation as well as the main program for this // application. #define SQLCA_STORAGE_CLASS extern #include <sqlca.h> // Include the emp class specification in the implementation of the // class body as well as the application program that makes use of it. EXEC SQL INCLUDE empclass.h; emp::emp() { // The scope of this WHENEVER statement spans the entire module. // Note that the error handler function is really a member function // of the emp class. EXEC SQL WHENEVER SQLERROR DO emp_error(); EXEC SQL ALLOCATE :emp_cursor; // Constructor - ALLOCATE Cursor. } emp::~emp() { EXEC SQL FREE :emp_cursor; // Destructor - FREE Cursor. } void emp::open() { EXEC SQL EXECUTE BEGIN emp_package.open_cursor(:emp_cursor); END; END-EXEC; } void emp::close() { EXEC SQL CLOSE :emp_cursor; } void emp::fetch() throw (int) { EXEC SQL FETCH :emp_cursor INTO :ename, :empno; if (sqlca.sqlcode == 1403) throw sqlca.sqlcode; // Like a WHENEVER NOT FOUND statement. } void emp::emp_error() { printf("%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc); EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL ROLLBACK WORK RELEASE; exit(1); }
The main program, cppdemo2.pc
, uses the cursor variable:
// Pro*C/C++ sample program demonstrating a simple use of Cursor Variables // implemented within a C++ class framework. Build this program as follows // // 1. Execute the cppdemo2.sql script within SQL*Plus // 2. Precompile the empclass.pc program as follows // > proc code=cpp sqlcheck=full user=scott/tiger lines=yes empclass // 3. Precompile the cppdemo2.pc program as follows // > proc code=cpp lines=yes cppdemo2 // 4. Compile and Link // // Note that you may have to specify various include directories using the // include option when precompiling. #include <stdio.h> #include <stdlib.h> #include <sqlca.h> static void sql_error() { printf("%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc); EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL ROLLBACK WORK RELEASE; exit(1); } // Physically include the emp class definition in this module. EXEC SQL INCLUDE empclass.h; int main() { EXEC SQL BEGIN DECLARE SECTION; char *uid = "scott/tiger"; EXEC SQL END DECLARE SECTION; EXEC SQL WHENEVER SQLERROR DO sql_error(); EXEC SQL CONNECT :uid; emp *e = new emp(); // Invoke Constructor - ALLOCATE Cursor Variable. e->open(); // Open the Cursor. while (1) { // Fetch from the Cursor, catching the NOT FOUND condition // thrown by the fetch() member function. try { e->fetch(); } catch (int code) { if (code == 1403) break; } printf("Employee: %s[%d]\n", e->ename, e->empno); } e->close(); // Close the Cursor. delete e; // Invoke Destructor - FREE Cursor Variable. EXEC SQL ROLLBACK WORK RELEASE; return (0); }
/* * cppdemo3.pc : An example of C++ Inheritance * * This program finds all salesman and prints their names * followed by how much they earn in total (ie; including * any commissions). */ #include <iostream.h> #include <stdio.h> #include <sqlca.h> #include <string.h> #define NAMELEN 10 class employee { // Base class is a simple employee public: char ename[NAMELEN]; int sal; employee(char *, int); }; employee::employee(char *ename, int sal) { strcpy(this->ename, ename); this->sal = sal; } // A salesman is a kind of employee class salesman : public employee { int comm; public: salesman(char *, int, int); friend ostream& operator<<(ostream&, salesman&); }; // Inherits employee attributes salesman::salesman(char *ename, int sal, int comm) : employee(ename, sal), comm(comm) {} ostream& operator<<(ostream& s, salesman& m) { return s << m.ename << m.sal + m.comm << endl; } void print(char *ename, int sal, int comm) { salesman man(ename, sal, comm); cout << man; } main() { EXEC SQL BEGIN DECLARE SECTION; char *uid = "scott/tiger"; char ename[NAMELEN]; int sal, comm; short comm_ind; EXEC SQL END DECLARE SECTION; EXEC SQL WHENEVER SQLERROR GOTO error; EXEC SQL CONNECT :uid; EXEC SQL DECLARE c CURSOR FOR SELECT ename, sal, comm FROM emp WHERE job = 'SALESMAN' ORDER BY ename; EXEC SQL OPEN c; cout << "Name Salary" << endl << "------ ------" << endl; EXEC SQL WHENEVER NOT FOUND DO break; while(1) { EXEC SQL FETCH c INTO :ename, :sal, :comm:comm_ind; print(ename, sal, (comm_ind < 0) ? 0 : comm); } EXEC SQL CLOSE c; exit(0); error: cout << endl << sqlca.sqlerrm.sqlerrmc << endl; exit(1); }