From: | Andreas Seltenreich <andreas+pg(at)gate450(dot)dyndns(dot)org> |
---|---|
To: | Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> |
Cc: | Kevin Murphy <murphy(at)genome(dot)chop(dot)edu>, PostgreSQL general <pgsql-general(at)postgresql(dot)org> |
Subject: | Re: STL problem in stored procedures |
Date: | 2005-10-25 17:02:02 |
Message-ID: | 87acgxjzsl.fsf@gate450.dyndns.org |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-general |
Tom Lane schrob:
> Kevin Murphy <murphy(at)genome(dot)chop(dot)edu> writes:
>> Tom Lane wrote:
>>> I think he needs to rewrite in C :-(. The backend is not C++ and I
>>> fear it's unlikely that libc++ will play nicely as a dynamic add-on.
>
>> It would be great if some C++/C guru could make a thorough analysis of
>> C++ integration issues.
>
> The main thing that scares me is lack of integration of C error handling
> (setjmp/longjmp) with C++ error handling (catch/throw). In a
> self-contained module you could perhaps avoid this by not using
> catch/throw, but I don't think you get to ignore the issue if you are
> using STL modules.
>
> It *might* work to put a generic "catch/report via elog" handler around
> each one of your entry-point functions. Haven't tried it.
Hmm, this setup worked quite stable here for some smaller educational
projects. The snippet I used to wrap the C++ was:
--8<---------------cut here---------------start------------->8---
#include <stdexcept>
extern "C" {
#include <postgres.h>
#include <fmgr.h>
}
extern "C" {
PG_FUNCTION_INFO_V1(cpptest);
Datum cpptest(PG_FUNCTION_ARGS);
}
Datum
cpptest(PG_FUNCTION_ARGS)
{
try {
throw std::runtime_error("Your code here.");
}
catch (std::exception &e) {
elog(ERROR, "caught C++ exception: %s", e.what());
}
catch (...) {
elog(ERROR, "caught non-standard C++ exception.");
}
}
--8<---------------cut here---------------end--------------->8---
I was even able to spare the exception code and the extern "C"s in
each and every function by hacking together a language handler loading
functors (classes overloading the operator "()") using the class
loader documented in this paper:
<http://www.s11n.net/papers/classloading_cpp.html> (this one is
exceptional in that it contains no demangling code for symbol lookup
and doesn't need any non-portable libraries).
> This all assumes that libc++ can cooperate with, rather than try to
> replace, libc in the first place. That would depend a lot on what
> platform you are on --- I think it might work OK with modern glibc,
> but my last experience with C++ was back when it wasn't so.
The most annoying non-cooperation for me was the STL's ignorance of
Pg's MemoryContexts. I managed to tame it with a wrapper around palloc
with an STL allocator interface:
--8<---------------cut here---------------start------------->8---
// $Id: pg_alloc.hh,v 1.2 2005/10/19 09:59:13 andreas Exp $
//
// This implements an allocator template with STL interface as a
// wrapper around PostgreSQL's MemoryContexts.
//
// It enables STL containers to use appropriate MemoryContexts instead
// of C++'s "free store".
#include <stdexcept>
extern "C" {
#include <postgres.h>
}
template <class T, MemoryContext *M = &CurrentMemoryContext>
class pg_allocator
{
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
pointer address(reference r) { return &r; }
const_pointer address(const_reference r) { return &r; }
pg_allocator() throw() { };
template <class U> pg_allocator(const pg_allocator<U, M>&) throw() { };
~pg_allocator() throw() { };
pointer allocate(size_type n, const T *hint = 0) throw(std::bad_alloc);
void deallocate(pointer p, size_type n);
void construct(pointer p, const T& val) { new(p) T(val); }
void destroy(pointer p) { p->~T(); }
size_type max_size() const throw();
template <class U>
struct rebind {typedef pg_allocator<U, M> other; };
};
template <class T, MemoryContext *M>
T* pg_allocator<T, M>::allocate(size_type n, const T * hint) throw(std::bad_alloc)
{
void* ptr = 0;
ptr = MemoryContextAlloc(*M, (n * sizeof(T)));
if (ptr)
return static_cast<T*>(ptr);
throw std::bad_alloc();
}
template <class T, MemoryContext *M>
void pg_allocator<T, M>::deallocate(T* ptr, size_t n)
{
pfree(static_cast<void *>(ptr));
}
template <class T, MemoryContext *M>
bool operator==(const pg_allocator<T, M>& a, const pg_allocator<T, M>& b) {
return true;
}
template <class T, MemoryContext *M>
bool operator!=(const pg_allocator<T, M>& a, const pg_allocator<T, M>& b) {
return false;
}
--8<---------------cut here---------------end--------------->8---
E.g., to allocate a list of strings on the CurrentMemoryContext:
list<string, pg_allocator<string>> foo;
You can supply a custom context with the second template argument if
you need, e.g., a session-wide cache of something.
At this level, I find C++ quite usable as a compromise between C and
the comparatively slow procedural languages. Of course, the interface
to PostgreSQL is still all PODs (plain old datatypes), but C++'s C
compatibility should be seen as a strength here.
regards,
Andreas
--
From | Date | Subject | |
---|---|---|---|
Next Message | Richard Huxton | 2005-10-25 17:11:49 | Re: Deleting vs foreign keys |
Previous Message | Steve Atkins | 2005-10-25 16:54:00 | Re: Select all invalid e-mail addresses |