Programmer's Guide to the Oracle Pro*C/C++ Precompiler Release 8.0 A54661_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 new 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 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);
}