33.13. Приложения C++#
33.13. Приложения C++ #
ECPG имеет ограниченную поддержку для приложений на C++. В этом разделе описаны некоторые ограничения.
Препроцессор ecpg
принимает входной файл, написанный на языке C (или на что-то похожем на C), содержащий встроенные команды SQL. Он преобразует встроенные команды SQL в фрагменты кода на языке C и, наконец, генерирует файл .c
. Объявления заголовочных файлов функций библиотеки, используемых фрагментами кода на языке C, которые генерирует ecpg
, оборачиваются в блоки extern "C" { ... }
, когда используются в C++, поэтому они должны работать без проблем в C++.
В общем, однако, препроцессор ecpg
понимает только язык C; он не обрабатывает специальный синтаксис и зарезервированные слова языка C++. Поэтому некоторый встроенный SQL-код, написанный в коде приложения на C++, который использует сложные функции, специфичные для C++, может быть неправильно предобработан или может не работать ожидаемым образом.
Безопасным способом использования встроенного SQL-кода в приложении на C++ является скрытие вызовов ECPG в модуле на C, который вызывается кодом приложения на C++, чтобы получить доступ к базе данных, и связывание его с остальным кодом на C++. См. Раздел 33.13.2 об этом.
33.13.1. Область для переменных хоста #
Препроцессор ecpg
понимает область видимости переменных в языке C. В языке C это довольно просто, потому что область видимости переменных основана на блоках кода. Однако в C++ переменные-члены класса ссылается на другой блок кода, отличный от объявленной позиции, поэтому препроцессор ecpg
не будет понимать область видимости переменных-членов класса.
Например, в следующем случае препроцессор ecpg
не сможет найти объявление для переменной dbname
в методе test
,
поэтому произойдет ошибка.
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); }
33.13.2. Разработка приложений на C++ с использованием внешнего модуля на C #
Если вы понимаете эти технические ограничения препроцессора ecpg
в C++, вы можете прийти к выводу, что связывание объектов C и объектов C++ на этапе связывания для использования функций ECPG в приложениях на C++ может быть лучше, чем написание некоторых встроенных SQL-команд в коде на C++ напрямую. В этом разделе описывается способ отделения некоторых встроенных 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; }
Для построения приложения выполните следующие действия. Преобразуйте test_mod.pgc
в test_mod.c
, запустив ecpg
, и сгенерируйте test_mod.o
, скомпилировав test_mod.c
с помощью компилятора C:
ecpg -o test_mod.c test_mod.pgc cc -c test_mod.c -o test_mod.o
Далее, сгенерируйте test_cpp.o
, скомпилировав
test_cpp.cpp
с помощью компилятора C++:
c++ -c test_cpp.cpp -o test_cpp.o
Наконец, свяжите эти объектные файлы, test_cpp.o
и test_mod.o
, в один исполняемый файл, используя драйвер компилятора C++:
c++ test_cpp.o test_mod.o -lecpg -o test_cpp