From: | Craig Ringer <craig(at)postnewspapers(dot)com(dot)au> |
---|---|
To: | Bruce Momjian <bruce(at)momjian(dot)us> |
Cc: | Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Igor <igor(at)carcass(dot)ath(dot)cx>, pgsql-general(at)postgresql(dot)org |
Subject: | Re: server-side extension in c++ |
Date: | 2010-06-02 02:11:37 |
Message-ID: | 4C05BDD9.1000205@postnewspapers.com.au |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-general |
On 02/06/10 09:23, Bruce Momjian wrote:
> Tom Lane wrote:
>> Craig Ringer <craig(at)postnewspapers(dot)com(dot)au> writes:
>>> On 01/06/10 11:05, Tom Lane wrote:
>>>> I'd be interested to see a section like this written by someone who'd
>>>> actually done a nontrivial C++ extension and lived to tell the tale.
>>
>>> I can't speak up there - my own C++/Pg backend stuff has been fairly
>>> trivial, and has been where I can maintain a fairly clean separation of
>>> the C++-exposed and the Pg-backend-exposed parts. I was able to keep
>>> things separate enough that my C++ compilation units didn't include the
>>> Pg backend headers; they just exposed a pure C public interface. The Pg
>>> backend-using compilation units were written in C, and talked to the C++
>>> part over its exposed pure C interfaces.
>>
>> Yeah, if you can design your code so that C++ never has to call back
>> into the core backend, that eliminates a large chunk of the pain.
>> Should we be documenting design ideas like this one?
>
> I have incorporated the new ideas into the C++ documentation section,
> and removed the comment block in the attached patch.
If you're going to include that much, I'd still really want to warn
people about exception/error handling too. It's important. I made brief
mention of it before, but perhaps some more detail would help if people
really want to do this.
( BTW, all in all, I agree with Tom Lane - the best answer is "don't".
Sometimes you need to access functionality from C++ libraries, but
unless that's your reason I wouldn't ever consider doing it. )
Here's a rough outline of the rules I follow when mixing C/C++ code,
plus some info on the longjmp error handling related complexities added
by Pg:
Letting an exception thrown from C++ code cross into C code will be
EXTREMELY ugly. The C++-to-C boundaries *must* have unconditional catch
blocks to convert thrown exceptions into appropriate error codes, even
if the C++ code in question never knowingly throws an exception. C++ may
throw std::bad_alloc on failure of operator new(), among other things,
so the user must _always_ have an unconditional catch. Letting an
exception propagate out to the C-based Pg backend is rather likely to
result in a backend crash.
If the C++ libraries you are using will put up with it, compile your C++
code with -fno-exceptions to make your life much, much easier, as you
can avoid worrying about this entirely. OTOH, you must then check for
NULL return from operator new().
If you can't do that: My usual rule is that any "extern C" function
*must* have an unconditional catch. I also require that any function
that may be passed as a function pointer to C code must be "extern C"
and thus must obey the previous rule, so that covers function pointers
and dlopen()ed access to functions.
Similarly, calling Pg code that may use Pg's error handling from within
C++ is unsafe. It should be OK if you know for absolute certain that the
C++ call tree in question only has plain-old-data (POD) structs and
simple variables on the stack, but even then it requires caution. C++
code that uses Pg calls can't do anything it couldn't do if you were
using 'goto' and labels in each involved function, but additionally has
to worry about returning and passing non-POD objects between functions
in a call chain by value, as a longjmp may result in dtors not being
properly called.
The best way to get around this issue is not to call into the Pg backend
from C++ code at all, instead encapsulating your C++ functionality into
cleanly separated modules with pure C interfaces. If you don't #include
any Pg backend headers into any compilation units compiled with the C++
compiler, that should do the trick.
If you must mix Pg calls and C++, restrict your C++ objects to the heap
(ie use pointers to them, managed with new and delete) and limit your
stack to POD variables (simple structs and built-in types). Note that
this means you can't use std::auto_ptr, std::tr1:shared_ptr, RAII lock
management, etc in C++ code that may call into the Pg backend.
--
Craig Ringer
Tech-related writing: http://soapyfrogs.blogspot.com/
From | Date | Subject | |
---|---|---|---|
Next Message | Ken Tanzer | 2010-06-02 02:21:27 | Re: Disable executing external commands from psql? |
Previous Message | Stephen Frost | 2010-06-02 02:10:56 | Re: Disable executing external commands from psql? |