ECPG 对 C++ 应用程序有一定的支持。本节介绍一些需要注意的问题。
ecpg
预处理器获取一个用 C(或类似 C 内容)和嵌入式 SQL 命令编写的输入文件,将嵌入式 SQL 命令转换成 C 语言块,最后生成一个 .c
文件。ecpg
生成的 C 语言块使用的库函数的头文件声明在使用 C++ 时被封装在 extern "C" { ... }
块中,因此它们在 C++ 中应该可以无缝地工作。
不过,一般来说,ecpg
预处理器仅了解 C 语言;它不处理 C++ 语言的特殊语法和保留字。因此,使用特定于 C++ 的复杂功能编写在 C++ 应用程序代码中的某些嵌入式 SQL 代码可能无法正确预处理或可能无法按预期方式工作。
在 C++ 应用程序中使用嵌入式 SQL 代码的可靠方式是在 C 模块中隐藏 ECPG 调用,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 命令。本节通过一个简单的示例说明了如何使用 C++ 应用程序代码分离某些嵌入式 SQL 命令。在此示例中,应用程序是在 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