2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (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;
}

要构建应用程序,请按以下步骤操作。通过运行 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

提交更正

如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用此表单报告文档问题。