Pro*C/C++ Precompiler Programmer's Guide Release 8.0 A58233-01 |
Library | Product | Contents | Index

# 7
Using C++

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:

- Understanding C++ Support
- Precompiling for C++
- Sample Programs

## Understanding C++ Support

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:

- Full C preprocessor support. You can use #define, #include, #ifdef, and other preprocessor directives in your Pro*C/C++ program, to handle constructs that the precompiler itself must process. See "Support for the C Preprocessor" on page 3-2 for more information.
- No need for EXEC SQL ... DECLARE statements to surround host variables. This allows full ANSI C standard function prototyping when function parameters are host variables. See the section "Declaring Host Variables" on page 3-27.
- Use of native C structures as host variables, including the ability to pass structs (or pointers to structs) as host variables to functions, and write functions that return host structures or struct pointers. See "Structure Pointers" on page 3-42 for more information.

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.

### No Special Macro Processing

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.

## Precompiling for C++

To control precompilation so that it accommodates C++, there are four considerations:

- Code emission by the precompiler
- Parsing capability
- The output filename extension
- The location of system header files

### Code Emission

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

- Generates C++ compilable code.
- Gives the output file a platform-specific file extension (suffix), such as ".C" or ".cc", rather than the standard ".c" extension. (You can override this by using the CPP_SUFFIX option.)
- Causes the value of the PARSE option to default to PARTIAL. You can also specify PARSE=NONE. If you specify PARSE=FULL, an error is issued at precompile time.
- Allows the use of the C++ style // Comments in your code. This style of Commenting is also permitted inside SQL statements and PL/SQL blocks when CODE=CPP.
- Pro*C/C++ recognizes SQL optimizer hints that begin with //+.

See Chapter 9, "Running the Pro*C/C++ Precompiler" for information about the KR_C and ANSI_C values for the CODE option.

### Parsing Code

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:

#####

PARSE=NONE | The value NONE has the following effects: C preprocessor directives are understood only inside a declare section. You must declare all host variables inside a Declare Section. Precompiler release 1.x behavior
PARSE=PARTIAL | The value PARTIAL has the following effects: All preprocessor directives are understood You must declare all host variables inside a Declare Section This option value is the default if CODE=CPP
PARSE=FULL | The value FULL has the following effects: The precompiler C parser runs on your code. All Preprocessor directives are understood. You can declare host variables at any place that they can be declared legally in C.

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.

### Output Filename Extension

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.

### System Header Files

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.)

## Sample Programs

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

    /*  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

    /*  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

    /*
     * 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);
    }

Prev | Next
| Copyright © 1997 Oracle Corporation. All Rights Reserved. |
Library | Product | Contents | Index