Postgresql 中文操作指南
36.13. C++ Applications #
ECPG 对 C++ 应用程序具有一定程度的支持。本节描述一些注意事项。
ecpg 预处理器读取以 C(或类似 C 的语言)编写的输入文件和嵌入式 SQL 命令,将嵌入式 SQL 命令转换成 C 语言块,最后生成 .c 文件。在 C 语言中使用 C 预处理器用 ecpg 生成程序时,用于 C 语言块的库函数的标头文件声明以 extern "C" { … } 块打包,以便在 C 中无缝运行。
但是,总的来说,ecpg 预处理器只理解 C;它不处理 C 语言的特殊语法和保留字。所以,在 C 应用程序代码中编写的部分嵌入式 SQL 代码中,某些使用特定于 C++ 的复杂功能可能会预处理失败或可能不能按预期工作。
在 C 应用程序中使用嵌入式 SQL 代码的安全方法是将 ECPG 调用隐藏在 C 模块中,C 应用程序代码调用该模块来访问数据库,并将其与 C++ 代码的其余部分链接在一起。请参见 Section 36.13.2了解相关信息。
36.13.1. Scope for Host Variables #
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);
}
36.13.2. C++ Application Development with External C Module #
如果您理解 ecpg 预处理器在 C 中的这些技术限制,则可能会得出结论,在链接阶段链接 C 对象和 C++ 对象以使 C 应用程序能够使用 ECPG 功能会优于直接在 C 代码中编写某些嵌入式 SQL 命令。本节使用一个简单的示例描述了一种用带嵌入式 SQL 命令的 C 模块分离 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