选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1
历史版本PostgreSQL 9.4 已于 2020 年 2 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

33.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++ 代码链接起来。详见第 33.13.2 节

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

33.13.2. 使用外部 C 模块的 C++ 应用开发 #

了解ecpg预处理器在 C++ 中的这些技术限制后,可能会认为:在链接阶段将 C 和 C++ 的目标文件链接起来,使 C++ 应用能够使用 ECPG 功能,会比直接在 C++ 代码中编写嵌入式 SQL 命令更好。本节用一个简单示例介绍如何将嵌入式 SQL 命令与 C++ 应用代码分离。在这个示例中,应用用 C++ 实现,连接 PostgreSQL 服务器的部分则使用 C 和 ECPG。

需要创建三种文件: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包含。由于需要从 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;
}

要构建该应用,按以下步骤处理。通过运行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

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。