2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: 開發版
不支援的版本: 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++ 應用程式開發 #

如果您理解 ecpg 預處理器在 C++ 中的這些技術限制,您可能會得出結論,在連結階段連結 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;
}

要構建應用程式,請按以下步驟進行。透過執行 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

提交更正

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