Postgresql 中文操作指南
36.13. C++ Applications #
ECPG 对 C++ 应用程序具有一定程度的支持。本节描述一些注意事项。
ECPG has some limited support for C++ applications. This section describes some caveats.
ecpg 预处理器读取以 C(或类似 C 的语言)编写的输入文件和嵌入式 SQL 命令,将嵌入式 SQL 命令转换成 C 语言块,最后生成 .c 文件。在 C 语言中使用 C 预处理器用 ecpg 生成程序时,用于 C 语言块的库函数的标头文件声明以 extern "C" { … } 块打包,以便在 C 中无缝运行。
The ecpg preprocessor takes an input file written in C (or something like C) and embedded SQL commands, converts the embedded SQL commands into C language chunks, and finally generates a .c file. The header file declarations of the library functions used by the C language chunks that ecpg generates are wrapped in extern "C" { … } blocks when used under C, so they should work seamlessly in C.
但是,总的来说,ecpg 预处理器只理解 C;它不处理 C 语言的特殊语法和保留字。所以,在 C 应用程序代码中编写的部分嵌入式 SQL 代码中,某些使用特定于 C++ 的复杂功能可能会预处理失败或可能不能按预期工作。
In general, however, the ecpg preprocessor only understands C; it does not handle the special syntax and reserved words of the C language. So, some embedded SQL code written in C application code that uses complicated features specific to C++ might fail to be preprocessed correctly or might not work as expected.
在 C 应用程序中使用嵌入式 SQL 代码的安全方法是将 ECPG 调用隐藏在 C 模块中,C 应用程序代码调用该模块来访问数据库,并将其与 C++ 代码的其余部分链接在一起。请参见 Section 36.13.2了解相关信息。
A safe way to use the embedded SQL code in a C application is hiding the ECPG calls in a C module, which the C application code calls into to access the database, and linking that together with the rest of the C++ code. See Section 36.13.2 about that.
36.13.1. Scope for Host Variables #
ecpg 预处理器理解 C 语言中变量的作用域。在 C 语言中,这比较简单,因为变量的作用域基于其代码块。然而,在 C++ 中,类成员变量引用不同的代码块中的声明位置,所以 ecpg 预处理器将不会理解类成员变量的作用域。
The ecpg preprocessor understands the scope of variables in C. In the C language, this is rather simple because the scopes of variables is based on their code blocks. In C++, however, the class member variables are referenced in a different code block from the declared position, so the ecpg preprocessor will not understand the scope of the class member variables.
例如,在以下情况下,ecpg 预处理器无法在 test 方法中找到变量 dbname 的任何声明,所以会发生错误。
For example, in the following case, the ecpg preprocessor cannot find any declaration for the variable dbname in the test method, so an error will occur.
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;
}
这段代码将导致以下类似的错误:
This code will result in an error like this:
ecpg test_cpp.pgc
test_cpp.pgc:28: ERROR: variable "dbname" is not declared
为了避免此作用域问题,可以将 test 方法修改为将本地变量用作中间存储。但是,这种方法仅仅是一种权宜之计,因为它使代码变得丑陋并且降低了性能。
To avoid this scope issue, the test method could be modified to use a local variable as intermediate storage. But this approach is only a poor workaround, because it uglifies the code and reduces performance.
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 服务器。
If you understand these technical limitations of the ecpg preprocessor in C, you might come to the conclusion that linking C objects and C objects at the link stage to enable C applications to use ECPG features could be better than writing some embedded SQL commands in C code directly. This section describes a way to separate some embedded SQL commands from C application code with a simple example. In this example, the application is implemented in C, while C and ECPG is used to connect to the PostgreSQL server.
必须创建三类文件:一个 C 文件 (*.pgc), 一个头文件以及一个 C++ 文件:
Three kinds of files have to be created: a C file (*.pgc), a header file, and a C++ file:
-
test_mod.pgc #
-
A sub-routine module to execute SQL commands embedded in C. It is going to be converted into test_mod.c by the preprocessor.
-
#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 #
-
A header file with declarations of the functions in the C module (test_mod.pgc). It is included by test_cpp.cpp. This file has to have an extern "C" block around the declarations, because it will be linked from the C++ module.
-
#ifdef __cplusplus
extern "C" {
#endif
void db_connect();
void db_test();
void db_disconnect();
#ifdef __cplusplus
}
#endif
-
test_cpp.cpp #
-
The main code for the application, including the main routine, and in this example a C++ class.
-
#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:
To build the application, proceed as follows. Convert test_mod.pgc into test_mod.c by running ecpg, and generate test_mod.o by compiling test_mod.c with the C compiler:
ecpg -o test_mod.c test_mod.pgc
cc -c test_mod.c -o test_mod.o
接下来,通过使用 C++ 编译器编译 test_cpp.cpp 生成 test_cpp.o:
Next, generate test_cpp.o by compiling test_cpp.cpp with the C++ compiler:
c++ -c test_cpp.cpp -o test_cpp.o
最后,使用 C++ 编译器驱动程序将这些目标文件 test_cpp.o 和 test_mod.o 链接成一个可执行文件:
Finally, link these object files, test_cpp.o and test_mod.o, into one executable, using the C++ compiler driver:
c++ test_cpp.o test_mod.o -lecpg -o test_cpp