36.13.C++应用程序#
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++ 代码的其余部分链接在一起。请参阅第 36.13.2 节。
36.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);
}
36.13.2. 使用外部 C 模块进行 C++ 应用程序开发#
如果您了解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