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 节。
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); }
如果您理解 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
如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表格 报告文档问题。