ヘッダーをスキップ
Pro*C/C++プログラマーズ・ガイド
11gリリース2(11.2)
B61343-01
  ドキュメント・ライブラリへ
ライブラリ
製品リストへ
製品
目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

12 C++アプリケーション

この章では、Pro*C/C++プリコンパイラを使用してC++の埋込みSQLアプリケーションをプリコンパイルする方法と、Pro*C/C++がC++互換コードを生成する仕組みについて説明します。この章の項目は、次のとおりです。

C++サポートの理解

Pro*C/C++でC++がどのようにサポートされているかを理解するには、Pro*C/C++の基本機能を理解する必要があります。特に、Pro*C/C++とPro*Cバージョン1との違いを認識する必要があります。

Pro*C/C++の基本機能は、次のとおりです。

Cプリプロセッサの機能をサポートし、特殊な宣言部の外でホスト変数を宣言できるようにするために、Pro*C/C++には完全なCパーサーが組み込まれています。Pro*C/C++パーサーはCパーサーであり、C++コードは解析できません。

したがって、C++をサポートするには、Cパーサーを完全または部分的に使用禁止にする必要があります。Cパーサーを使用禁止にするために、Pro*C/C++プリコンパイラには、Pro*C/C++がソース・コードに対して行うC解析の範囲を制御できるコマンドライン・オプションが組み込まれています。

特殊なマクロ処理は不要

C++をPro*C/C++とともに使用する場合、特殊な事前処理や、Pro*C/C++外部の特殊なマクロ・プロセッサは必要ありません。プリコンパイラの出力に対してマクロ・プロセッサを実行しなくても、C++との互換性を実現できます。

Pro*C/C++プリコンパイラの旧リリースのユーザーで、プリコンパイラの出力にマクロ・プロセッサを使用していた場合は、コードを変更しないでPro*C/C++を使用してC++アプリケーションをプリコンパイルできます。

C++のプリコンパイル

C++に対応できるようにプリコンパイルを制御するには、次の4点を考慮する必要があります。

コードの生成

プリコンパイラで生成されるコードの種類(C互換コードまたはC++互換コード)を指定する必要があります。デフォルトでは、Pro*C/C++によってC言語のコードが生成されます。C++は、C言語の完全なスーパーセットではありません。生成されるコードをC++のコンパイラでコンパイルするには、コードを多少変更する必要があります。

たとえば、プリコンパイラでは、アプリケーション・コードが出力されるのみでなく、ランタイム・ライブラリSQLLIBに対するコールが挿入されます。SQLLIB内の関数は、C関数です。特殊なC++版のSQLLIBはありません。このため、C++コンパイラを使用して生成したコードをコンパイルするには、SQLLIB内でコールした関数をPro*C/C++によりC関数として宣言する必要があります。

C言語の出力では、プリコンパイラにより次のようなプロトタイプが生成されます。

void sqlora(unsigned long *, void *);

ただし、C++互換コードの場合には、プリコンパイラで次のようなコードを生成する必要があります。

extern "C" {
void sqlora(unsigned long *, void *);
};

Pro*C/C++によって生成されるコードの種類は、CODEというプリコンパイラ・オプションを使用して制御します。このオプションの値は、CPP、KR_CおよびANSI_Cの3つです。これらのオプションの違いは、SQLLIB関数sqloraの宣言方法がCODEオプションの3つの値で異なることを考えると説明できます。

void sqlora( /*_ unsigned long *, void * _*/);  /* K&R C */

void sqlora(unsigned long *, void *);           /* ANSI C */

extern "C" {                                    /* CPP */
void sqlora(unsigned long *, void *);
};

CODE=CPPを指定すると、プリコンパイラは次を行います。

  • C++互換コードを生成します。

  • 出力ファイルに、標準の「.c」拡張子ではなく、「.C」や「.cc」など、プラットフォーム固有のファイル拡張子(接尾辞)を付けます。(この設定は、CPP_SUFFIXオプションを使用して上書きできます。)

  • PARSEオプションの値をデフォルトのPARTIALにします。また、PARSE=NONEも指定できます。PARSE=FULLを指定すると、プリコンパイル時にエラーが発生します。

  • C++形式の//コメントをコード内で使用可能にします。CODE=CPPのときは、この形式のコメントをSQL文およびPL/SQLブロックの中でも使用できます。

  • Pro*C/C++は//+で始まるSQLオプティマイザ・ヒントを認識できます。

  • Object Type Translator(OTT)によって生成されるヘッダー・ファイルは、宣言部の中にインクルードする必要があります。


    関連項目

    CODEオプションの値KR_CとANSI_Cの詳細は、「CODE」を参照してください。

コードの解析

使用しているコードへのPro*C/C++のCパーサーの効果を制御する必要があります。この制御はPARSEプリコンパイラ・オプションを使用することで可能になります。このオプションでは、プリコンパイラのCパーサーがコードの取扱方法を制御できます。

PARSEオプションの値と効果を次に示します。

表12-1 PARSEオプションの値と効果

効果

PARSE=NONE

値NONEの効果は次のとおりです。

  • Cプリプロセッサ・ディレクティブは、宣言部の中にある場合のみ解釈されます。

  • ホスト変数はすべて、宣言部の中に宣言する必要があります。

  • プリコンパイラ・リリース1.xの動作。

PARSE=PARTIAL

値PARTIALの効果は次のとおりです。

  • すべてのプリプロセッサ・ディレクティブが解釈されます。

  • ホスト変数はすべて、宣言部の中に宣言する必要があります。

このオプション値は、CODE=CPPのときデフォルトです。

PARSE=FULL

値FULLの効果は次のとおりです。

  • プリコンパイラのCパーサーがコード上で動作します。

  • すべてのプリプロセッサ・ディレクティブが解釈されます。

  • ホスト変数は、C言語で有効に宣言できる位置であればどこにでも宣言できます。


このオプション値は、CODEオプションの値がCPP以外のときのデフォルト値です。CODE=CPPのときにPARSE=FULLを指定すると、エラーになります。

C++互換コードを生成するには、PARSEオプションにNONEまたはPARTIALのどちらかを指定する必要があります。PARSE=FULLのときはCパーサーが動作し、コードにあるC++クラスなどの構文は認識されません。

出力ファイル名の拡張子

ほとんどのCコンパイラでは、入力ファイルのデフォルト拡張子は「.c」とみなされます。ただし、C++コンパイラでは、想定されるファイルの拡張子がコンパイラごとに異なる場合があります。CPP_SUFFIXオプションを使用すると、プリコンパイラで生成されるファイルの拡張子を指定できます。このオプションの値は、引用符もピリオドも付けない文字列です。たとえば、CPP_SUFFIX=ccまたはCPP_SUFFIX=Cのように指定します。

システム・ヘッダー・ファイル

Pro*C/C++では、プラットフォーム固有の標準的な位置でstdio.hなどの標準のシステム・ヘッダー・ファイルが検索されます。Pro*C/C++では、hppまたはh++などの拡張子が付いたヘッダー・ファイルは検索されません。たとえば、ほとんどのUNIXシステムではstdio.hファイルのフルパス名は/usr/include/stdio.hです。

ただし、C++コンパイラは独自のバージョンのstdio.hを持っており、これはシステムの標準位置にはありません。C++でのプリコンパイル時には、Pro*C/C++でシステム・ヘッダー・ファイルを検索できるように、SYS_INCLUDEプリコンパイラ・オプションを使用してディレクトリ・パスを指定する必要があります。次に例を示します。

SYS_INCLUDE=(/usr/lang/SC2.0.1/include,/usr/lang/SC2.1.1/include)

システム・ヘッダー・ファイル以外の位置を指定するには、INCLUDEプリコンパイラ・オプションを使用します。SYS_INCLUDEオプションで指定したディレクトリは、INCLUDEオプションで指定したディレクトリよりも前に検索されます。「INCLUDE」も参照してください。

PARSE=NONEのときは、Pro*C/C++はシステム・ヘッダー・ファイルをインクルードする必要がないため、システム・ファイルについてSYS_INCLUDEおよびINCLUDEで指定した値は無視されます。(ただし、当然ながら、sqlca.hなどのPro*C/C++固有のヘッダーは、EXEC SQL INCLUDE文を使用してインクルードできます。)

サンプル・プログラム

この項には、C++構造体を含んでいるサンプルのPro*C/C++プログラムを3つ記載しています。これらのプログラムはそれぞれdemoディレクトリにオンラインで利用可能な形で入っています。

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 or
// PARSE={PARTIAL|NONE} or both.
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 preceding 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

次のアプリケーションは、簡単なモジュール化の例です。最初にSQL*Plusの次のSQLスクリプトcppdemo2.sqlを実行します。

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
/

ヘッダー・ファイルempclass.hでは、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 using 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;
};

empclass.pcのコードには、empメソッドが含まれています。

#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);
}

メイン・プログラムcppdemo2.pcでは、カーソル変数が使用されます。

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

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