ECPG(Oracle Pro*c Compatible) has some limited support for C++ applications. This section describes some caveats.
The "ecpg(Oracle Pro*c compatible) preprocessor takes an input file
written in C (or something like C) and embedded SQL commands,
converts the embedded SQL commands into C language chunks, and
finally generates a .c file. The header file
declarations of the library functions used by the C language chunks
that "ecpg(Oracle Pro*c compatible) generates are wrapped
in extern "C" { ... } blocks when used under
C++, so they should work seamlessly in C++.
In general, however, the "ecpg(Oracle Pro*c compatible) preprocessor only
understands C; it does not handle the special syntax and reserved
words of the C++ language. So, some embedded SQL code written in
C++ application code that uses complicated features specific to C++
might fail to be preprocessed correctly or might not work as
expected.
A safe way to use the embedded SQL code in a C++ application is hiding the ECPG(Oracle Pro*c Compatible) calls in a C module, which the C++ application code calls into to access the database, and linking that together with the rest of the C++ code. See Section 34.13.2 about that.
The "ecpg(Oracle Pro*c compatible) preprocessor understands the scope of
variables in C. In the C language, this is rather simple because
the scopes of variables is based on their code blocks. In C++,
however, the class member variables are referenced in a different
code block from the declared position, so
the "ecpg(Oracle Pro*c compatible) preprocessor will not understand the
scope of the class member variables.
For example, in the following case, the "ecpg(Oracle Pro*c compatible)
preprocessor cannot find any declaration for the
variable dbname in the test
method, so an error will occur.
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;
}
This code will result in an error like this:
ecpg test_cpp.pgc
test_cpp.pgc:28: ERROR: variable "dbname" is not declared
To avoid this scope issue, the test method
could be modified to use a local variable as intermediate storage.
But this approach is only a poor workaround, because it uglifies
the code and reduces 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);
}
If you understand these technical limitations of
the "ecpg(Oracle Pro*c compatible) preprocessor in C++, you might come to
the conclusion that linking C objects and C++ objects at the link
stage to enable C++ applications to use ECPG(Oracle Pro*c Compatible) features could be
better than writing some embedded SQL commands in C++ code
directly. This section describes a way to separate some embedded
SQL commands from C++ application code with a simple example. In
this example, the application is implemented in C++, while C and
ECPG(Oracle Pro*c Compatible) is used to connect to the LightDB server.
Three kinds of files have to be created: a C file
(*.pgc), a header file, and a C++ file:
test_mod.pgc
A sub-routine module to execute SQL commands embedded in C.
It is going to be converted
into test_mod.c by the preprocessor.
#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
A header file with declarations of the functions in the C
module (test_mod.pgc). It is included by
test_cpp.cpp. This file has to have an
extern "C" block around the declarations,
because it will be linked from the C++ module.
#ifdef __cplusplus
extern "C" {
#endif
void db_connect();
void db_test();
void db_disconnect();
#ifdef __cplusplus
}
#endif
test_cpp.cpp
The main code for the application, including
the main routine, and in this example a
C++ class.
#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;
}
To build the application, proceed as follows. Convert
test_mod.pgc into test_mod.c by
running "ecpg(Oracle Pro*c compatible), and generate
test_mod.o by compiling
test_mod.c with the C compiler:
ecpg -o test_mod.c test_mod.pgc cc -c test_mod.c -o test_mod.o
Next, generate test_cpp.o by compiling
test_cpp.cpp with the C++ compiler:
c++ -c test_cpp.cpp -o test_cpp.o
Finally, link these object files, test_cpp.o
and test_mod.o, into one executable, using the C++
compiler driver:
c++ test_cpp.o test_mod.o -lecpg -o test_cpp