33.13. Приложения C++#

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