On this page
35.13. C 应用程序
ECPG 对 C 应用程序的支持有限。本节介绍一些注意事项。
ecpg
预处理器接收用 C(或类似 C 的语言)编写的 Importing 文件和嵌入式 SQL 命令,将嵌入式 SQL 命令转换为 C 语言块,最后生成.c
文件。 ecpg
生成的 C 语言块使用的库函数的头文件声明在 C 下使用时包装在extern "C" { ... }
块中,因此它们应在 C 中无缝工作。
但是,一般而言,ecpg
预处理程序只理解 C;它不处理 C 语言的特殊语法和保留字。因此,使用 C 特有的复杂功能用 C 应用程序代码编写的某些嵌入式 SQL 代码可能无法正确地预处理,或者可能无法按预期工作。
在 C 应用程序中使用嵌入式 SQL 代码的一种安全方法是将 ECPG 调用隐藏在 C 模块中,C 应用程序代码将其调用以访问数据库,并将其与其余 C 代码链接在一起。参见Section 35.13.2。
35 .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);
}
35 .13.2. 使用外部 C 模块进行 C 应用程序开发
如果了解 C 中ecpg
预处理器的这些技术局限性,您可能会得出以下结论:在链接阶段链接 C 对象和 C 对象以使 C 应用程序能够使用 ECPG 功能可能比用 C 代码编写一些嵌入式 SQL 命令更好。直。本节通过一个简单的示例介绍了从 C 应用程序代码中分离某些嵌入式 SQL 命令的方法。在此示例中,该应用程序以 C 实现,而 C 和 ECPG 用于连接到 PostgreSQL 服务器。
必须创建三种文件:C 文件(*.pgc
),头文件和 C 文件:
test_mod.pgc
- 一个子例程模块,用于执行嵌入在 C 中的 SQL 命令。它将由预处理器转换为
test_mod.c
。
- 一个子例程模块,用于执行嵌入在 C 中的 SQL 命令。它将由预处理器转换为
#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 模块进行链接。
- 具有 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