PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 15.6 » Interfaces client » ECPG -- SQL embarqué en C » Applications C++

36.13. Applications C++

ECPG a un support limité pour les applications C++. Cette section décrit certains pièges.

Le préprocesseur ecpg prend un fichier d'entrée écrit en C (ou quelque chose qui ressemble à du C) et des commandes SQL embarquées, et convertit les commandes SQL embarquées dans des morceaux de langage, et finalement génère un fichier .c. Les déclarations de fichiers d'entête des fonctions de librairie utilisées par les morceaux de langage C que génère ecpg sont entourées de blocs extern "C" { ... } quand ils sont utilisés en C++, ils devraient donc fonctionner de façon transparente en C++.

En général, toutefois, le préprocesseur ecpg ne comprend que le C; il ne gère pas la syntaxe spéciale et les mots réservés du langage C++. Par conséquent, du code SQL embarqué écrit dans du code d'une application C++ qui utilise des fonctionnalités compliquées spécifiques au C++ pourrait ne pas être préprocessé correctement ou pourrait ne pas fonctionner comme prévu.

Une façon sûre d'utiliser du code SQL embarqué dans une application C++ est de cacher les appels à ECPG dans un module C, que le code C++ de l'application appelle pour accéder à la base, et lier ce module avec le reste du code C++. Voyez Section 36.13.2 à ce sujet.

36.13.1. Portée des Variable Hôtes

Le préprocesseur ecpg comprend la porté des variables C. Dans le langage C, c'est plutôt simple parce que la portée des variables ne dépend que du bloc de code dans lequel elle se trouve. En C++, par contre, les variables d'instance sont référencées dans un bloc de code différent de la position de déclaration, ce qui fait que le préprocesseur ecpg ne comprendra pas la portée des variables d'instance.

Par exemple, dans le cas suivant, le préprocesseur ecpg ne peut pas trouver de déclaration pour la variable dbname dans la méthode test, une erreur va donc se produire.

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;
}
   

Ce code génèrera une erreur comme celle qui suit :

ecpg test_cpp.pgc
test_cpp.pgc:28: ERROR: variable "dbname" is not declared
   

Pour éviter ce problème de portée, la méthode test pourrait être modifiée pour utiliser une variable locale comme stockage intermédiaire. Mais cette approche n'est qu'un mauvais contournement, parce qu'elle rend le code peu élégant et réduit la performance.

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. Développement d'application C++ avec un Module Externe en C

Si vous comprenez ces limitations techniques du préprocesseur ecpg en C++, vous arriverez peut-être à la conclusion que lier des objets C et C++ au moment du link pour permettre à des applications C++ d'utiliser les fonctionnalités d'ECPG pourrait être mieux que d'utiliser des commandes SQL embarquées dans du code C++ directement. Cette section décrit un moyen de séparer des commandes SQL embarquées du code d'une application C++ à travers un exemple simple. Dans cet exemple, l'application est implémentée en C++, alors que C et ECPG sont utilisés pour se connecter au serveur PostgreSQL.

Trois types de fichiers devront être créés: un fichier C (*.pgc), un fichier d'entête, et un fichier C++ :

test_mod.pgc

Un module de routines pour exécuter des commandes SQL embarquées en C. Il sera converti en test_mod.c par le préprocesseur.

#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

Un fichier d'entête avec les déclarations des fonctions du module C (test_mod.pgc). Il est inclus par test_cpp.cpp. Ce fichier devra avoir un bloc extern "C" autour des déclarations, parce qu'il sera lié à partir d'un module C++.

#ifdef __cplusplus
extern "C" {
#endif

void db_connect();
void db_test();
void db_disconnect();

#ifdef __cplusplus
}
#endif
       

test_cpp.cpp

Le code principal de l'application, incluant la routine main, et dans cet exemple une classe 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;
}
       

Pour construire l'application, procédez comme suit. Convertissez test_mod.pgc en test_mod.c en lançant ecpg, et générez test_mod.o en compilant test_mod.c avec le compilateur C:

ecpg -o test_mod.c test_mod.pgc
cc -c test_mod.c -o test_mod.o
   

Puis, générez test_cpp.o en compilant test_cpp.cpp avec le compilateur C++:

c++ -c test_cpp.cpp -o test_cpp.o
   

Finalement, liez ces objets, test_cpp.o et test_mod.o, dans un exécutable, en utilisant le compilateur C++:

c++ test_cpp.o test_mod.o -lecpg -o test_cpp