ECPGはC++アプリケーションを多少の制限がありますがサポートします。 本節ではいくつかの注意を説明します。
ecpg
プリプロセッサはC(またはCのようなもの)と埋め込みSQLコマンドで記述された入力ファイルを取り、埋め込みSQLコマンドをC言語の小塊に変換し、最終的に.c
ファイルを作成します。
ecpg
が生成するC言語の小塊で使用されるライブラリ関数のヘッダファイル定義は、C++で使用される場合extern "C" { ... }
で囲まれます。
このためC++でも継ぎ目なく動作するはずです。
しかし一般的には、ecpg
プリプロセッサはCのみを理解しています。
C++言語の特殊な構文や予約語を取り扱いません。
このため、C++に特化した複雑な機能を使用するC++アプリケーションコードの中に記述された埋め込みSQLコードの一部は、正しく前処理することに失敗する、または想定通りに動作しないかもしれません。
C++アプリケーションで埋め込みSQLコードを使用する安全な方法は、ECPGの呼び出しをCモジュール内に隠蔽し、残りのC++コードとまとめてリンクすることです。C++アプリケーションコードがデータベースにアクセスするためにはそのCモジュールを呼び出します。 35.13.2を参照してください。
ecpg
プリプロセッサはCにおける変数のスコープを理解しています。
C言語では、変数のスコープはコードブロックに基づきますので、どちらかといえば単純です。
しかしC++では
クラスメンバ変数は宣言場所とは異なるコードブロック内で参照されます。
このためecpg
プリプロセッサはクラスメンバ変数のスコープを理解していません。
例えば、以下の場合、ecpg
プリプロセッサはtest
メソッド内のdbname
変数の定義を見つけることができません。
このためエラーになります。
class TestCpp { EXEC SQL BEGIN DECLARE SECTION; char dbname[1024]; EXEC SQL END DECLARE SECTION; public: TestCpp(); void test(); ~TestCpp(); }; TestCpp::TestCpp() { EXEC SQL CONNECT TO testdb1; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; } void Test::test() { EXEC SQL SELECT current_database() INTO :dbname; printf("current_database = %s\n", dbname); } TestCpp::~TestCpp() { EXEC SQL DISCONNECT ALL; }
このコードは以下のようなエラーになります。
ecpg test_cpp.pgc
test_cpp.pgc:28: ERROR: variable "dbname" is not declared
このスコープ問題を回避するためには、test
メソッドを中間格納領域としてローカル変数を使用するように変更することができます。
しかしこの手法は悪い回避策でしかありません。コードを醜くしますし性能も劣化させます。
void TestCpp::test() { EXEC SQL BEGIN DECLARE SECTION; char tmp[1024]; EXEC SQL END DECLARE SECTION; EXEC SQL SELECT current_database() INTO :tmp; strlcpy(dbname, tmp, sizeof(tmp)); printf("current_database = %s\n", dbname); }
C++におけるecpg
の技術的な制限を理解しているのであれば、ECPG機能を使用するC++アプリケーションを実現するためには、リンク段階でCオブジェクトとC++オブジェクトをリンクする方が、C++コード内で埋め込みSQLコマンドを直接記述することより優れているという結論に至るでしょう。
本節では、簡単な例を用いて、C++アプリケーションコードから埋め込みSQLコマンドを分離する方法について説明します。
この例では、アプリケーションはC++で実装し、PostgreSQLサーバに接続するためにCおよびECPGを使用します。
Cファイル(*.pgc
)、ヘッダファイル、C++ファイルという3種類のファイルを作成しなければなりません。
test_mod.pgc
C内に埋め込まれたサブルーチンモジュールです。
プリプロセッサによりtest_mod.c
に変換されます。
#include "test_mod.h" #include <stdio.h> void db_connect() { EXEC SQL CONNECT TO testdb1; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; } void db_test() { EXEC SQL BEGIN DECLARE SECTION; char dbname[1024]; EXEC SQL END DECLARE SECTION; EXEC SQL SELECT current_database() INTO :dbname; printf("current_database = %s\n", dbname); } void db_disconnect() { EXEC SQL DISCONNECT ALL; }
test_mod.h
Cモジュール(test_mod.pgc
)内の関数宣言を持つヘッダファイルです。
test_cpp.cpp
でインクルードされます。
このファイルは、C++モジュールからリンクされますので、宣言を囲むextern "C"
ブロックを持たなければなりません。
#ifdef __cplusplus extern "C" { #endif void db_connect(); void db_test(); void db_disconnect(); #ifdef __cplusplus } #endif
test_cpp.cpp
main
ルーチンとこの例でのC++クラスを含む、アプリケーションの主要コードです。
#include "test_mod.h" class TestCpp { public: TestCpp(); void test(); ~TestCpp(); }; TestCpp::TestCpp() { db_connect(); } void TestCpp::test() { db_test(); } TestCpp::~TestCpp() { db_disconnect(); } int main(void) { TestCpp *t = new TestCpp(); t->test(); return 0; }
アプリケーションを構築するためには、以下の処理を行います。
ecpg
を実行してtest_mod.pgc
をtest_mod.c
に変換します。
そしてCコンパイラを用いてtest_mod.c
をコンパイルしtest_mod.o
を生成します。
ecpg -o test_mod.c test_mod.pgc cc -c test_mod.c -o test_mod.o
次にC++コンパイラを用いてtest_cpp.cpp
をコンパイルしtest_cpp.o
を生成します。
c++ -c test_cpp.cpp -o test_cpp.o
最後に、C++コンパイラドライバを用いてtest_cpp.o
およびtest_mod.o
というオブジェクトファイルを実行形式ファイルにリンクします。
c++ test_cpp.o test_mod.o -lecpg -o test_cpp