ECPG a un support limité pour les applications C++. Cette section décrit des 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 35.13.2, « Développement d'application C++ avec un Module Externe en C » à ce sujet.
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; } 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); }
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++:
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; } 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; }
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
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 constuire 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