Re: STL problem in stored procedures

Поиск
Список
Период
Сортировка
От Andreas Seltenreich
Тема Re: STL problem in stored procedures
Дата
Msg-id 87acgxjzsl.fsf@gate450.dyndns.org
обсуждение исходный текст
Ответ на Re: STL problem in stored procedures  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: STL problem in stored procedures  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-general
Tom Lane schrob:

> Kevin Murphy <murphy@genome.chop.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
--

В списке pgsql-general по дате отправления:

Предыдущее
От: Steve Atkins
Дата:
Сообщение: Re: Select all invalid e-mail addresses
Следующее
От: Richard Huxton
Дата:
Сообщение: Re: Deleting vs foreign keys