支援的版本: 目前 (17) / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1

34.13. C++應用程式 #

ECPG 對 C++ 應用程式有一些有限的支援。本節描述了一些注意事項。

ecpg 前置處理器採用以 C 語言(或類似 C 語言)和嵌入式 SQL 命令編寫的輸入檔案,將嵌入式 SQL 命令轉換為 C 語言區塊,最後產生一個 .c 檔案。當在 C++ 中使用時,ecpg 產生的 C 語言區塊使用的函式庫函式標頭檔案宣告會包裝在 extern "C" { ... } 區塊中,因此它們應該可以在 C++ 中無縫運作。

然而,一般來說,ecpg 前置處理器只理解 C 語言;它不處理 C++ 語言的特殊語法和保留字。因此,在 C++ 應用程式程式碼中編寫的一些使用 C++ 特有複雜功能的嵌入式 SQL 程式碼,可能無法正確地進行前置處理,或者可能無法如預期運作。

在 C++ 應用程式中使用嵌入式 SQL 程式碼的安全方法是將 ECPG 呼叫隱藏在 C 模組中,C++ 應用程式程式碼呼叫該模組以存取資料庫,並將其與其餘的 C++ 程式碼連結在一起。有關此資訊,請參閱第 34.13.2 節

34.13.1. 主機變數的作用域 #

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

34.13.2. 使用外部 C 模組開發 C++ 應用程式 #

如果您瞭解 C++ 中 ecpg 前置處理器的這些技術限制,您可能會得出結論,在連結階段連結 C 物件和 C++ 物件,以使 C++ 應用程式能夠使用 ECPG 功能,可能比直接在 C++ 程式碼中編寫一些嵌入式 SQL 命令更好。本節描述了一種使用簡單範例將一些嵌入式 SQL 命令與 C++ 應用程式程式碼分離的方法。在此範例中,應用程式以 C++ 實作,而 C 和 ECPG 用於連接到 PostgreSQL 伺服器。

必須建立三種檔案:C 檔案 (*.pgc)、標頭檔案和 C++ 檔案

test_mod.pgc #

一個子常式模組,用於執行嵌入在 C 中的 SQL 命令。它將由前置處理器轉換為 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 包含。此檔案必須在宣告周圍有一個 extern "C" 區塊,因為它將從 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;
}

若要建置應用程式,請按以下步驟進行。透過執行 ecpgtest_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.otest_mod.o 連結到一個可執行檔中

c++ test_cpp.o test_mod.o -lecpg -o test_cpp

提交更正

如果您在文件中看到任何不正確、與特定功能的使用體驗不符或需要進一步說明的內容,請使用 此表單 回報文件問題。