Препроцессор ECPG имеет ограниченную поддержку приложений на языке C++. В данном разделе описаны некоторые ограничения.
Параметр ecpg препроцессор ECPG принимает входной файл,
написанный на языке C (или аналогичном) и содержащий внедренные SQL-команды,
преобразует внедренные SQL-команды в фрагменты кода на языке C и
в конечном итоге генерирует .c файл. Объявления в заголовочных файлах для библиотечных функций, используемых фрагментами кода на языке C, которые ecpg создает препроцессор ECPG, обернуты
в extern "C" { ... } блоки при использовании в среде C++, поэтому они должны корректно работать в C++.
В целом, однако, ecpg препроцессор поддерживает только язык C; он не обрабатывает специальный синтаксис и зарезервированные слова языка C++. Таким образом, некоторый код внедренного SQL, написанный в приложении на C++ с использованием специфичной для C++ функциональности, может быть обработан препроцессором некорректно или работать не так, как ожидалось.
Безопасный способ использования внедренного SQL в приложении на C++ заключается в сокрытии вызовов препроцессора ECPG в модуле на языке C, к которому приложение на C++ обращается для доступа к базе данных, с последующей компоновкой этого модуля с остальным кодом на C++. См. Раздел 4.3.13.2 об этом.
Данный 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);
}
Если вы понимаете данные технические ограничения
препроцессора ECPG при работе с C++, ecpg вы можете прийти к выводу, что компоновка объектных файлов C и C++ на этапе связывания для использования возможностей препроцессора ECPG в приложениях на C++ может быть более эффективным решением, чем написание команд внедренного SQL непосредственно в коде C++. В данном разделе на простом примере описывается способ отделения некоторых команд внедренного SQL от кода приложения на языке C++. В данном примере приложение реализовано на языке C++, в то время как язык C и препроцессор ECPG используются для установки соединения с сервером PostgreSQL.
Необходимо создать файлы трех типов: файл на языке C
(*.pgc), заголовочный файл и файл на языке C++:
test_mod.pgc #
Модуль подпрограммы для выполнения команд SQL, внедренных в код на языке C.
Он будет преобразован
в test_mod.c препроцессором.
#include "test_mod.h" #includevoid 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