Pro*C/C++ Precompiler Programmer's Guide Release 8.0 A58233-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++" on page 7-3 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 Chapter 9, "Running the Pro*C/C++ Precompiler" 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. 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" on page 9-22. 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); }
/* cppdemo2.pc: Dynamic SQL Method 3 * * This program uses dynamic SQL Method 3 to retrieve * the names of all employees in a given department * from the EMP table. */ #include <iostream.h> #include <stdio.h> #include <string.h> #define USERNAME "SCOTT" #define PASSWORD "TIGER" /* Include the SQL Communications Area, a structure through * which ORACLE makes runtime status information such as error * codes, warning flags, and diagnostic text available to the * program. Also include the ORACA. */ #include <sqlca.h> #include <oraca.h> // The ORACA=YES option must be specified // to enable use of the ORACA EXEC ORACLE OPTION (ORACA=YES); EXEC SQL BEGIN DECLARE SECTION; char *username = USERNAME; char *password = PASSWORD; VARCHAR sqlstmt[80]; VARCHAR ename[10]; int deptno = 10; EXEC SQL END DECLARE SECTION; void sql_error(char *msg); main() { // Call sql_error() function on any error // in an embedded SQL statement EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error"); // Save text of SQL current statement in // the ORACA if an error occurs. oraca.orastxtf = ORASTFERR; // Connect to Oracle. EXEC SQL CONNECT :username IDENTIFIED BY :password; cout << endl << "Connected to Oracle." << endl << endl; /* Assign a SQL query to the VARCHAR sqlstmt. Both the * array and the length parts must be set properly. Note * that the query contains one host-variable placeholder, * v1, for which an actual input host variable must be * supplied at OPEN time. */ strcpy((char *)sqlstmt.arr, "SELECT ename FROM emp WHERE deptno = :v1"); sqlstmt.len = strlen((char *)sqlstmt.arr); /* Display the SQL statement and its current input host * variable. */ cout << (char *)sqlstmt.arr << endl; cout << " v1 = " << deptno << endl << endl <<"Employee" << endl << "--------" << endl; /* The PREPARE statement associates a statement name with * a string containing a SELECT statement. The statement * name is a SQL identifier, not a host variable, and * therefore does not appear in the Declare Section. * * A single statement name can be PREPAREd more than once, * optionally FROM a different string variable. */ EXEC SQL PREPARE S FROM :sqlstmt; /* The DECLARE statement associates a cursor with a * PREPAREd statement. The cursor name, like the statement * name, does not appear in the Declare Section. * A single cursor name can not be DECLAREd more than once. */ EXEC SQL DECLARE C CURSOR FOR S; /* The OPEN statement evaluates the active set of the * PREPAREd query USING the specified input host variables, * which are substituted positionally for placeholders in * the PREPAREd query. For each occurrence of a * placeholder in the statement there must be a variable * in the USING clause. That is, if a placeholder occurs * multiple times in the statement, the corresponding * variable must appear multiple times in the USING clause. * * The USING clause can be omitted only if the statement * contains no placeholders. OPEN places the cursor at the * first row of the active set in preparation for a FETCH. * * A single DECLAREd cursor can be OPENed more than once, * optionally USING different input host variables. */ EXEC SQL OPEN C USING :deptno; /* Break the loop when all data have been retrieved. */ EXEC SQL WHENEVER NOT FOUND DO break; /* Loop until the NOT FOUND condition is detected. */ while (1) { /* The FETCH statement places the select list of the * current row into the variables specified by the INTO * clause, then advances the cursor to the next row. If * there are more select-list fields than output host * variables, the extra fields will not be returned. * Specifying more output host variables than select-list * fields results in an ORACLE error. */ EXEC SQL FETCH C INTO :ename; /* Null-terminate the array before output. */ ename.arr[ename.len] = '\0'; cout << (char *)ename.arr << endl; } /* Print the cumulative number of rows processed by the * current SQL statement. */ printf("\nQuery returned %d row%s.\n\n", sqlca.sqlerrd[2], (sqlca.sqlerrd[2] == 1) ? "" : "s"); /* The CLOSE statement releases resources associated with * the cursor. */ EXEC SQL CLOSE C; /* Commit any pending changes and disconnect from Oracle. */ EXEC SQL COMMIT RELEASE; cout << "Have a good day!" << endl << endl; exit(0); } void sql_error(char *msg) { cout << endl << msg << endl; sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; oraca.orastxt.orastxtc[oraca.orastxt.orastxtl] = '\0'; oraca.orasfnm.orasfnmc[oraca.orasfnm.orasfnml] = '\0'; cout << sqlca.sqlerrm.sqlerrmc << endl; cout << "in " << oraca.orastxt.orastxtc << endl; cout << "on line " << oraca.oraslnr << " of " << oraca.orasfnm.orasfnmc << endl << endl; /* Disable ORACLE error checking to avoid an infinite loop * should another error occur within this routine. */ EXEC SQL WHENEVER SQLERROR CONTINUE; // Release resources associated with the cursor. EXEC SQL CLOSE C; // Roll back any pending changes and disconnect from Oracle. EXEC SQL ROLLBACK RELEASE; exit(1); }
/* * 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); }