aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--makefile34
-rw-r--r--odb/core.hxx7
-rw-r--r--odb/database.cxx14
-rw-r--r--odb/database.hxx71
-rw-r--r--odb/database.ixx12
-rw-r--r--odb/database.txx73
-rw-r--r--odb/exception.cxx17
-rw-r--r--odb/exception.hxx22
-rw-r--r--odb/exceptions.cxx10
-rw-r--r--odb/exceptions.hxx52
-rw-r--r--odb/forward.hxx38
-rw-r--r--odb/makefile60
-rw-r--r--odb/meta/answer.hxx25
-rw-r--r--odb/meta/class-p.hxx28
-rw-r--r--odb/meta/polymorphic-p.hxx51
-rw-r--r--odb/meta/remove-c.hxx27
-rw-r--r--odb/meta/remove-cv.hxx24
-rw-r--r--odb/meta/remove-p.hxx27
-rw-r--r--odb/meta/remove-v.hxx27
-rw-r--r--odb/session.cxx120
-rw-r--r--odb/session.hxx190
-rw-r--r--odb/session.txx320
-rw-r--r--odb/shared-ptr-traits.hxx119
-rw-r--r--odb/shared-ptr.hxx145
-rw-r--r--odb/shared-ptr/base.cxx63
-rw-r--r--odb/shared-ptr/base.hxx91
-rw-r--r--odb/shared-ptr/base.ixx79
-rw-r--r--odb/shared-ptr/base.txx183
-rw-r--r--odb/traits.hxx93
-rw-r--r--odb/transaction.cxx93
-rw-r--r--odb/transaction.hxx139
-rw-r--r--odb/transaction.ixx25
32 files changed, 2273 insertions, 6 deletions
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..32b7548
--- /dev/null
+++ b/makefile
@@ -0,0 +1,34 @@
+# file : makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+# license : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))build/bootstrap.make
+
+default := $(out_base)/
+test := $(out_base)/.test
+install := $(out_base)/.install
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(default): $(out_base)/odb/ #$(out_base)/tests/
+
+# Test.
+#
+$(test): $(out_base)/tests/.test
+
+# Install.
+#
+$(install): $(out_base)/odb/.install
+ $(call install-data,$(src_base)/LICENSE,$(install_doc_dir)/libodb/LICENSE)
+ $(call install-data,$(src_base)/README,$(install_doc_dir)/libodb/README)
+
+# Clean.
+#
+$(clean): $(out_base)/odb/.clean $(out_base)/tests/.clean
+
+$(call include,$(bld_root)/install.make)
+
+$(call import,$(src_base)/odb/makefile)
+#$(call import,$(src_base)/tests/makefile)
diff --git a/odb/core.hxx b/odb/core.hxx
index 227dea7..ea0ed05 100644
--- a/odb/core.hxx
+++ b/odb/core.hxx
@@ -13,11 +13,6 @@
# define PRAGMA_ODB(x)
#endif
-namespace odb
-{
- class image
- {
- };
-}
+#include <odb/forward.hxx>
#endif // ODB_CORE_HXX
diff --git a/odb/database.cxx b/odb/database.cxx
new file mode 100644
index 0000000..aa75dae
--- /dev/null
+++ b/odb/database.cxx
@@ -0,0 +1,14 @@
+// file : odb/database.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/database.hxx>
+
+namespace odb
+{
+ database::
+ ~database ()
+ {
+ }
+}
diff --git a/odb/database.hxx b/odb/database.hxx
new file mode 100644
index 0000000..2a904a4
--- /dev/null
+++ b/odb/database.hxx
@@ -0,0 +1,71 @@
+// file : odb/database.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DATABASE_HXX
+#define ODB_DATABASE_HXX
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class database
+ {
+ public:
+ virtual
+ ~database ();
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T> obj);
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr
+ load (typename object_traits<T>::id_type const&);
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr
+ find (typename object_traits<T>::id_type const&);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T> obj);
+
+ template <typename T, template <typename> class P>
+ void
+ modified (P<T> obj);
+
+ // Transaction API.
+ //
+ public:
+ // Start a transaction. If an existing session can be obtained via
+ // session::current(), the transaction is run as part of that session.
+ // Otherwise a new session is created and will be automatically flushed
+ // and destroyed when transaction ends.
+ //
+ virtual transaction_impl*
+ begin_transaction () = 0;
+
+ // Start a transaction as part of an existing session. The session
+ // is not automatically flushed or destroyed when transaction ends.
+ //
+ virtual transaction_impl*
+ begin_transaction (session&) = 0;
+
+ protected:
+ database ();
+
+ private:
+ database (const database&);
+ database& operator= (const database&);
+ };
+}
+
+#include <odb/database.ixx>
+#include <odb/database.txx>
+
+#endif // ODB_DATABASE_HXX
diff --git a/odb/database.ixx b/odb/database.ixx
new file mode 100644
index 0000000..c8fe08c
--- /dev/null
+++ b/odb/database.ixx
@@ -0,0 +1,12 @@
+// file : odb/database.ixx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ inline database::
+ database ()
+ {
+ }
+}
diff --git a/odb/database.txx b/odb/database.txx
new file mode 100644
index 0000000..c507030
--- /dev/null
+++ b/odb/database.txx
@@ -0,0 +1,73 @@
+// file : odb/database.txx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb
+{
+ // @@ Should I make these inline?
+ //
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type database::
+ persist (P<T> p)
+ {
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename object_traits<T>::shared_ptr& obj (p);
+
+ session& s (transaction::current ().session ());
+ return s.persist<T> (*this, obj);
+ }
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr database::
+ load (typename object_traits<T>::id_type const& id)
+ {
+ typename object_traits<T>::shared_ptr r (find<T> (id));
+
+ if (object_traits<T>::shared_ops::null_ptr (r))
+ throw object_not_persistent ();
+
+ return r;
+ }
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr database::
+ find (typename object_traits<T>::id_type const& id)
+ {
+ session& s (transaction::current ().session ());
+ return s.find<T> (*this, id);
+ }
+
+ template <typename T, template <typename> class P>
+ void database::
+ erase (P<T> p)
+ {
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename object_traits<T>::shared_ptr& obj (p);
+
+ session& s (transaction::current ().session ());
+ return s.erase (*this, obj);
+ }
+
+ template <typename T, template <typename> class P>
+ void database::
+ modified (P<T> p)
+ {
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename object_traits<T>::shared_ptr& obj (p);
+
+ session& s (transaction::current ().session ());
+ return s.modified (obj);
+ }
+}
diff --git a/odb/exception.cxx b/odb/exception.cxx
new file mode 100644
index 0000000..52d8370
--- /dev/null
+++ b/odb/exception.cxx
@@ -0,0 +1,17 @@
+// file : odb/exception.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <typeinfo>
+
+#include <odb/exception.hxx>
+
+namespace odb
+{
+ char const* exception::
+ what () const throw ()
+ {
+ return typeid (*this).name ();
+ }
+}
diff --git a/odb/exception.hxx b/odb/exception.hxx
new file mode 100644
index 0000000..ff3fc63
--- /dev/null
+++ b/odb/exception.hxx
@@ -0,0 +1,22 @@
+// file : odb/exception.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTION_HXX
+#define ODB_EXCEPTION_HXX
+
+#include <exception>
+
+namespace odb
+{
+ struct exception: std::exception
+ {
+ // By default return the exception type name ( typeid (*this).name () ).
+ //
+ virtual char const*
+ what () const throw ();
+ };
+}
+
+#endif // ODB_EXCEPTION_HXX
diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx
new file mode 100644
index 0000000..d86657c
--- /dev/null
+++ b/odb/exceptions.cxx
@@ -0,0 +1,10 @@
+// file : odb/exceptions.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+
+namespace odb
+{
+}
diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx
new file mode 100644
index 0000000..4b5909b
--- /dev/null
+++ b/odb/exceptions.hxx
@@ -0,0 +1,52 @@
+// file : odb/exceptions.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTIONS_HXX
+#define ODB_EXCEPTIONS_HXX
+
+#include <odb/exception.hxx>
+
+namespace odb
+{
+ // nested_transaction
+ //
+ struct already_in_transaction: odb::exception
+ {
+ };
+
+ // no_transaction
+ //
+ struct not_in_transaction: odb::exception
+ {
+ };
+
+ // finilized_transaction
+ //
+ struct transaction_already_finilized: odb::exception
+ {
+ };
+
+ struct already_in_session: odb::exception
+ {
+ };
+
+ struct not_in_session: odb::exception
+ {
+ };
+
+ struct object_not_persistent: odb::exception
+ {
+ };
+
+ struct object_already_persistent: odb::exception
+ {
+ };
+
+ struct database_exception: odb::exception
+ {
+ };
+}
+
+#endif // ODB_EXCEPTIONS_HXX
diff --git a/odb/forward.hxx b/odb/forward.hxx
new file mode 100644
index 0000000..b59034f
--- /dev/null
+++ b/odb/forward.hxx
@@ -0,0 +1,38 @@
+// file : odb/forward.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_FORWARD_HXX
+#define ODB_FORWARD_HXX
+
+namespace odb
+{
+ class session;
+ class database;
+ class transaction;
+
+ template <typename T>
+ class shared_ptr;
+
+ class access
+ {
+ public:
+ template <typename T>
+ class value_traits;
+
+ template <typename T>
+ class object_traits;
+
+ template <typename T>
+ class object_memory;
+
+ template <typename T>
+ class object_factory;
+
+ template <typename P>
+ class shared_factory;
+ };
+}
+
+#endif // ODB_FORWARD_HXX
diff --git a/odb/makefile b/odb/makefile
new file mode 100644
index 0000000..71c719a
--- /dev/null
+++ b/odb/makefile
@@ -0,0 +1,60 @@
+# file : odb/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+# license : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
+
+cxx_tun := \
+exception.cxx \
+exceptions.cxx \
+shared-ptr/base.cxx \
+session.cxx \
+database.cxx \
+transaction.cxx
+
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+odb.l := $(out_base)/odb.l
+odb.l.cpp-options := $(out_base)/odb.l.cpp-options
+
+default := $(out_base)/
+install := $(out_base)/.install
+clean := $(out_base)/.clean
+
+
+# Build.
+#
+$(odb.l): $(cxx_obj)
+
+$(cxx_obj) $(cxx_od): $(odb.l.cpp-options)
+$(odb.l.cpp-options): value := -I$(src_root)
+
+$(call include-dep,$(cxx_od))
+
+
+# Convenience alias for default target.
+#
+$(out_base)/: $(odb.l)
+
+# Install.
+#
+$(install): $(odb.l)
+ $(call install-lib,$<,$(install_lib_dir)/$(ld_lib_prefix)odb$(ld_lib_suffix))
+ $(call install-dir,$(src_base),$(install_inc_dir)/odb,\
+'(' -name '*.hxx' -o -name '*.ixx' -o -name '*.txx' ')')
+
+# Clean.
+#
+$(clean): $(odb.l).o.clean \
+ $(odb.l.cpp-options).clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od))
+
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-l.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
diff --git a/odb/meta/answer.hxx b/odb/meta/answer.hxx
new file mode 100644
index 0000000..80a25e0
--- /dev/null
+++ b/odb/meta/answer.hxx
@@ -0,0 +1,25 @@
+// file : odb/meta/answer.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_ANSWER_HXX
+#define ODB_META_ANSWER_HXX
+
+namespace odb
+{
+ namespace meta
+ {
+ struct yes
+ {
+ char filling;
+ };
+
+ struct no
+ {
+ char filling[2];
+ };
+ }
+}
+
+#endif // ODB_META_ANSWER_HXX
diff --git a/odb/meta/class-p.hxx b/odb/meta/class-p.hxx
new file mode 100644
index 0000000..e3d15c9
--- /dev/null
+++ b/odb/meta/class-p.hxx
@@ -0,0 +1,28 @@
+// file : odb/meta/class-p.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_CLASS_HXX
+#define ODB_META_CLASS_HXX
+
+#include <odb/meta/answer.hxx>
+
+namespace odb
+{
+ namespace meta
+ {
+ // g++ cannot have these inside class_p.
+ //
+ template <typename Y> no class_p_test (...);
+ template <typename Y> yes class_p_test (void (Y::*) ());
+
+ template <typename X>
+ struct class_p
+ {
+ static bool const r = sizeof (class_p_test<X> (0)) == sizeof (yes);
+ };
+ }
+}
+
+#endif // ODB_META_CLASS_HXX
diff --git a/odb/meta/polymorphic-p.hxx b/odb/meta/polymorphic-p.hxx
new file mode 100644
index 0000000..571ef52
--- /dev/null
+++ b/odb/meta/polymorphic-p.hxx
@@ -0,0 +1,51 @@
+// file : odb/meta/polymorphic-p.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_POLYMORPHIC_HXX
+#define ODB_META_POLYMORPHIC_HXX
+
+#include <odb/meta/class-p.hxx>
+#include <odb/meta/remove-cv.hxx>
+
+namespace odb
+{
+ namespace meta
+ {
+ template <typename CVX>
+ struct polymorphic_p
+ {
+ typedef typename remove_cv<CVX>::r X;
+
+ template <typename Y, bool C>
+ struct impl
+ {
+ static const bool r = false;
+ };
+
+ template <typename Y>
+ struct impl<Y, true>
+ {
+ struct t1: Y
+ {
+ t1 ();
+ };
+
+ struct t2: Y
+ {
+ t2 ();
+
+ virtual
+ ~t2 () throw ();
+ };
+
+ static const bool r = sizeof (t1) == sizeof (t2);
+ };
+
+ static const bool r = impl<X, class_p<X>::r>::r;
+ };
+ }
+}
+
+#endif // ODB_META_POLYMORPHIC_HXX
diff --git a/odb/meta/remove-c.hxx b/odb/meta/remove-c.hxx
new file mode 100644
index 0000000..db724b3
--- /dev/null
+++ b/odb/meta/remove-c.hxx
@@ -0,0 +1,27 @@
+// file : odb/meta/remove-c.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_REMOVE_C_HXX
+#define ODB_META_REMOVE_C_HXX
+
+namespace odb
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_c
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_c<X const>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // ODB_META_REMOVE_C_HXX
diff --git a/odb/meta/remove-cv.hxx b/odb/meta/remove-cv.hxx
new file mode 100644
index 0000000..0805a9d
--- /dev/null
+++ b/odb/meta/remove-cv.hxx
@@ -0,0 +1,24 @@
+// file : odb/meta/remove-cv.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_REMOVE_CV_HXX
+#define ODB_META_REMOVE_CV_HXX
+
+#include <odb/meta/remove-c.hxx>
+#include <odb/meta/remove-v.hxx>
+
+namespace odb
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_cv
+ {
+ typedef typename remove_v<typename remove_c<X>::r>::r r;
+ };
+ }
+}
+
+#endif // ODB_META_REMOVE_CV_HXX
diff --git a/odb/meta/remove-p.hxx b/odb/meta/remove-p.hxx
new file mode 100644
index 0000000..053c860
--- /dev/null
+++ b/odb/meta/remove-p.hxx
@@ -0,0 +1,27 @@
+// file : odb/meta/remove-p.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_REMOVE_P_HXX
+#define ODB_META_REMOVE_P_HXX
+
+namespace odb
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_p
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_p<X*>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // ODB_META_REMOVE_P_HXX
diff --git a/odb/meta/remove-v.hxx b/odb/meta/remove-v.hxx
new file mode 100644
index 0000000..9e37a6c
--- /dev/null
+++ b/odb/meta/remove-v.hxx
@@ -0,0 +1,27 @@
+// file : odb/meta/remove-v.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_META_REMOVE_V_HXX
+#define ODB_META_REMOVE_V_HXX
+
+namespace odb
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_v
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_v<X volatile>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // ODB_META_REMOVE_V_HXX
diff --git a/odb/session.cxx b/odb/session.cxx
new file mode 100644
index 0000000..9ee0104
--- /dev/null
+++ b/odb/session.cxx
@@ -0,0 +1,120 @@
+// file : odb/session.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // std::make_pair
+
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb
+{
+ session::type_map::
+ ~type_map ()
+ {
+ }
+
+ session::object_proxy::
+ ~object_proxy ()
+ {
+ }
+
+ //
+ // session
+ //
+
+ static session* current_session = 0;
+
+ session::
+ session ()
+ {
+ if (current_session != 0)
+ throw already_in_session ();
+
+ current_session = this;
+ }
+
+ bool session::
+ has_current ()
+ {
+ return current_session != 0;
+ }
+
+ session& session::
+ current ()
+ {
+ if (current_session == 0)
+ throw not_in_session ();
+
+ return *current_session;
+ }
+
+ void session::
+ current (session& s)
+ {
+ current_session = &s;
+ }
+
+ void session::
+ reset_current ()
+ {
+ current_session = 0;
+ }
+
+ void session::
+ flush ()
+ {
+ if (!transaction::has_current ())
+ throw not_in_transaction ();
+
+ // @@ Order of insertion and deletion can be important (triggers,
+ // id assignment, constraints etc).
+ //
+
+ for (object_map::iterator i (object_map_.begin ()),
+ e (object_map_.end ()); i != e;)
+ {
+ object_proxy& pxy (*i->second);
+
+ switch (pxy.state_)
+ {
+ case object_proxy::transient:
+ {
+ pxy.persist ();
+
+ // If the id is auto-assigned, then we only get it now, so
+ // register with the id map.
+ //
+ if (pxy.id_source_ != ids_assigned)
+ pxy.register_id (id_map_, i);
+
+ pxy.state_ = object_proxy::clean;
+ ++i;
+ break;
+ }
+ case object_proxy::dirty:
+ {
+ pxy.update ();
+ pxy.state_ = object_proxy::clean;
+ ++i;
+ break;
+ }
+ case object_proxy::erased:
+ {
+ pxy.erase ();
+ pxy.unregister_id (id_map_);
+ object_map_.erase (i++);
+ break;
+ }
+ case object_proxy::clean:
+ {
+ // Nothing to do for this case.
+ //
+ ++i;
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/odb/session.hxx b/odb/session.hxx
new file mode 100644
index 0000000..90df00d
--- /dev/null
+++ b/odb/session.hxx
@@ -0,0 +1,190 @@
+// file : odb/session.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SESSION_HXX
+#define ODB_SESSION_HXX
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ class session
+ {
+ public:
+ session ();
+ ~session ()
+ {
+ reset_current ();
+ }
+
+ private:
+ session (const session&);
+ session& operator= (const session&);
+
+ public:
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (database&, P<T> obj);
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr
+ find (database&, typename object_traits<T>::id_type const&);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (database&, P<T> obj);
+
+ template <typename T, template <typename> class P>
+ void
+ modified (P<T> obj);
+
+ void
+ flush ();
+
+ public:
+ // Return true if there is a session in effect.
+ //
+ static bool
+ has_current ();
+
+ // Get current thread's session. Throw if no session in effect.
+ //
+ static session&
+ current ();
+
+ // Set current thread's session.
+ //
+ static void
+ current (session&);
+
+ // Revert to the no session in effect state.
+ //
+ static void
+ reset_current ();
+
+ private:
+ struct object_proxy;
+ typedef std::map< void*, shared_ptr<object_proxy> > object_map;
+
+ struct type_map
+ {
+ virtual
+ ~type_map () = 0;
+ };
+
+ template <typename T>
+ struct type_map_impl:
+ type_map,
+ std::map<typename object_traits<T>::id_type, object_map::iterator>
+ {
+ };
+
+ struct type_info_comparator
+ {
+ bool
+ operator() (const std::type_info* x, const std::type_info* y) const
+ {
+ // XL C++ on AIX has buggy type_info::before() in that
+ // it returns true for two different type_info objects
+ // that happened to be for the same type.
+ //
+#if defined(__xlC__) && defined(_AIX)
+ return *x != *y && x->before (*y);
+#else
+ return x->before (*y);
+#endif
+ }
+ };
+
+ typedef
+ std::map<const std::type_info*, shared_ptr<type_map>, type_info_comparator>
+ id_map;
+
+ //
+ //
+ struct object_proxy
+ {
+ virtual
+ ~object_proxy () = 0;
+
+ object_proxy (database& db, id_source is)
+ : id_source_ (is), db_ (db)
+ {
+ }
+
+ enum state
+ {
+ transient, /* Persisted out of transaction. */
+ clean,
+ dirty, /* To be updated on flush. */
+ erased /* To be deleted on flush. */
+ };
+
+ public:
+ virtual void
+ persist () = 0;
+
+ virtual void
+ update () = 0;
+
+ virtual void
+ erase () = 0;
+
+ virtual void
+ register_id (id_map&, object_map::iterator) = 0;
+
+ virtual void
+ unregister_id (id_map&) = 0;
+
+ public:
+ state state_;
+ id_source id_source_;
+ database& db_;
+ };
+
+ template <typename T>
+ struct object_proxy_impl: object_proxy
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ptr obj_ptr;
+ typedef typename traits::shared_ops ptr_ops;
+
+ object_proxy_impl (database& db, obj_ptr obj)
+ : object_proxy (db, traits::id_source), obj_ (obj)
+ {
+ }
+
+ public:
+ virtual void
+ persist ();
+
+ virtual void
+ update ();
+
+ virtual void
+ erase ();
+
+ virtual void
+ register_id (id_map&, object_map::iterator);
+
+ virtual void
+ unregister_id (id_map&);
+
+ public:
+ obj_ptr obj_;
+ };
+
+ object_map object_map_;
+ id_map id_map_;
+ };
+}
+
+#include <odb/session.txx>
+
+#endif // ODB_SESSION_HXX
diff --git a/odb/session.txx b/odb/session.txx
new file mode 100644
index 0000000..55155f7
--- /dev/null
+++ b/odb/session.txx
@@ -0,0 +1,320 @@
+// file : odb/session.txx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // std::make_pair
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+#include <iostream> // @@ tmp
+
+namespace odb
+{
+ //
+ // session::object_proxy_impl
+ //
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ persist ()
+ {
+ traits::insert (db_, ptr_ops::get_ref (obj_));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ update ()
+ {
+ traits::update (db_, ptr_ops::get_ref (obj_));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ erase ()
+ {
+ traits::erase (db_, traits::id (ptr_ops::get_ref (obj_)));
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ register_id (id_map& idm, object_map::iterator i)
+ {
+ // For polymorphic types we should use the root of the hierarchy
+ // (presumably defined in traits) as a type id so that all types
+ // in this hierarchy end up in the same map. See also other places.
+ //
+ shared_ptr<type_map>& m (idm[&typeid (T)]);
+
+ if (!m)
+ m.reset (new (shared) type_map_impl<T>);
+
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+
+ std::pair<typename type_map_impl<T>::iterator, bool> r (
+ mi.insert (std::make_pair (traits::id (ptr_ops::get_ref (obj_)), i)));
+
+ if (!r.second)
+ throw object_already_persistent ();
+ }
+
+ template <typename T>
+ void session::object_proxy_impl<T>::
+ unregister_id (id_map& idm)
+ {
+ shared_ptr<type_map>& m (idm[&typeid (T)]);
+
+ if (m)
+ {
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+ mi.erase (traits::id (ptr_ops::get_ref (obj_)));
+ }
+ }
+
+ //
+ // session
+ //
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type session::
+ persist (database& db, P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+ T& ref (ops::get_ref (obj));
+
+ // @@ What if the object that's already in the map is in the
+ // erased state?
+ //
+ if (object_map_.find (ptr) != object_map_.end ())
+ throw object_already_persistent ();
+
+ shared_ptr<object_proxy> pxy (new (shared) object_proxy_impl<T> (db, obj));
+
+ // If we are in a transaction, save this object immediately. This
+ // helps with two things: (1) assignment of auto-generated ids and
+ // (2) detection of duplicate objects.
+ //
+ if (transaction::has_current ())
+ {
+ // @@ What if the manually-assigned id is already in use by
+ // an object waiting to be deleted?
+ //
+ traits::insert (db, ref);
+ pxy->state_ = object_proxy::clean;
+ }
+ else
+ pxy->state_ = object_proxy::transient;
+
+ object_map::iterator i (
+ object_map_.insert (std::make_pair (ptr, pxy)).first);
+
+ // If this object has auto-assigned id and we haven't hit the db
+ // yet, then the id is "NULL" and we cannot insert this object
+ // into the id map.
+ //
+ if (pxy->state_ != object_proxy::transient ||
+ traits::id_source == ids_assigned)
+ {
+ try
+ {
+ pxy->register_id (id_map_, i);
+ }
+ catch (...)
+ {
+ object_map_.erase (i);
+ throw;
+ }
+ }
+
+ return traits::id (ref);
+ }
+
+ template <typename T>
+ typename object_traits<T>::shared_ptr session::
+ find (database& db, typename object_traits<T>::id_type const& id)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+ typedef typename traits::shared_ptr obj_ptr;
+
+ // First see if we have this object in the maps.
+ //
+ shared_ptr<type_map>& m (id_map_[&typeid (T)]);
+
+ if (m)
+ {
+ typedef type_map_impl<T> map_impl;
+ map_impl& mi (static_cast<map_impl&> (*m));
+ typename map_impl::iterator i (mi.find (id));
+
+ if (i != mi.end ())
+ {
+ const object_map::iterator& j (i->second);
+
+ if (j->second->state_ != object_proxy::erased)
+ {
+ object_proxy_impl<T>& pxy (
+ static_cast<object_proxy_impl<T>&> (*j->second));
+ return pxy.obj_;
+ }
+ else
+ return obj_ptr ();
+ }
+ }
+
+ // If we are in transaction then hit the database. Otherwise, the
+ // object is not found.
+ //
+ if (!transaction::has_current ())
+ return obj_ptr ();
+
+ obj_ptr obj (traits::find (db, id));
+
+ if (ops::null_ptr (obj))
+ return obj;
+
+ // Add this object to our maps.
+ //
+ void* ptr (ops::get_ptr (obj));
+ shared_ptr<object_proxy> pxy (new (shared) object_proxy_impl<T> (db, obj));
+ pxy->state_ = object_proxy::clean;
+
+ object_map::iterator i (
+ object_map_.insert (
+ std::make_pair (ptr, pxy)).first);
+
+ try
+ {
+ if (!m)
+ m.reset (new (shared) type_map_impl<T>);
+
+ type_map_impl<T>& mi (static_cast<type_map_impl<T>&> (*m));
+ mi.insert (std::make_pair (id, i));
+ }
+ catch (...)
+ {
+ object_map_.erase (i);
+ throw;
+ }
+
+ return obj;
+ }
+
+ template <typename T, template <typename> class P>
+ void session::
+ erase (database& db, P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+ T& ref (ops::get_ref (obj));
+
+ object_map::iterator i (object_map_.find (ptr));
+
+ if (object_map_.find (ptr) == object_map_.end ())
+ throw object_not_persistent ();
+
+ object_proxy& pxy (*i->second);
+
+ switch (pxy.state_)
+ {
+ case object_proxy::transient:
+ {
+ // If this object is still transient then all we need to do is
+ // purge it from the maps.
+ //
+
+ // See if we are registered in the id map.
+ //
+ if (traits::id_source == ids_assigned)
+ pxy.unregister_id (id_map_);
+
+ object_map_.erase (i);
+ break;
+ }
+ case object_proxy::clean:
+ case object_proxy::dirty:
+ {
+ // Ideally we would need to store the object id as well as the
+ // version (optimistic concurrency) since the user is free to
+ // modify the object state.
+ //
+ pxy.state_ = object_proxy::erased;
+ break;
+ }
+ case object_proxy::erased:
+ {
+ // Already erased. Throw to be consistent with the transient
+ // case.
+ //
+ throw object_not_persistent ();
+ }
+ }
+ }
+
+ template <typename T, template <typename> class P>
+ void session::
+ modified (P<T> p)
+ {
+ typedef object_traits<T> traits;
+ typedef typename traits::shared_ops ops;
+
+ // P<T> should be the same or convertible to
+ // object_traits<T>::shared_ptr.
+ //
+ const typename traits::shared_ptr& obj (p);
+
+ // For polymorphic types we need to cast the pointer to the
+ // root of the hierarchy.
+ //
+ void* ptr (ops::get_ptr (obj));
+
+ object_map::iterator i (object_map_.find (ptr));
+
+ if (object_map_.find (ptr) == object_map_.end ())
+ throw object_not_persistent ();
+
+ object_proxy& pxy (*i->second);
+
+ switch (pxy.state_)
+ {
+ case object_proxy::transient:
+ case object_proxy::dirty:
+ {
+ // Nothing to do here.
+ //
+ break;
+ }
+ case object_proxy::clean:
+ {
+ pxy.state_ = object_proxy::dirty;
+ break;
+ }
+ case object_proxy::erased:
+ {
+ throw object_not_persistent ();
+ }
+ }
+ }
+}
diff --git a/odb/shared-ptr-traits.hxx b/odb/shared-ptr-traits.hxx
new file mode 100644
index 0000000..a1d5146
--- /dev/null
+++ b/odb/shared-ptr-traits.hxx
@@ -0,0 +1,119 @@
+// file : odb/shared-ptr-traits.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SHARED_PTR_TRAITS_HXX
+#define ODB_SHARED_PTR_TRAITS_HXX
+
+#include <new> // operators new/delete
+#include <cstddef> // std::size_t
+
+#include <odb/shared-ptr.hxx>
+
+namespace odb
+{
+ template <typename P>
+ class shared_ptr_traits;
+
+ // Default implementation that should work for any sensible smart
+ // pointer with one template argument (object type). The only
+ // assumptions that we make are the availability of operator-> and
+ // operator*, and that the former does not throw if the pointer is
+ // NULL.
+ //
+ template <typename T, template <typename> class P>
+ class shared_ptr_traits< P<T> >
+ {
+ public:
+ typedef T type;
+ typedef P<T> shared_ptr;
+
+ // Return underlying pointer, including NULL.
+ //
+ static type*
+ get_ptr (const shared_ptr& p)
+ {
+ return p.operator-> ();
+ }
+
+ // Return reference to the pointed-to object.
+ //
+ static type&
+ get_ref (const shared_ptr& p)
+ {
+ return *p;
+ }
+
+ // Return true if the pointer is NULL.
+ //
+ static bool
+ null_ptr (const shared_ptr& p)
+ {
+ return get_ptr () == 0;
+ }
+
+ public:
+ // Allocate memory for a shared object.
+ //
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ // Free memory allocated for a shared object. This functions is
+ // only called if the constructor of the object being created
+ // fails. Otherwise, shared_ptr is used to delete the object
+ // and free the memory. This behavior is identical to the one
+ // used by operator delete overloading.
+ //
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for odb::shared_ptr.
+ //
+ template <typename T>
+ class shared_ptr_traits< shared_ptr<T> >
+ {
+ public:
+ typedef T type;
+ typedef odb::shared_ptr<T> shared_ptr;
+
+ static type*
+ get_ptr (const shared_ptr& p)
+ {
+ return p.get ();
+ }
+
+ static type&
+ get_ref (const shared_ptr& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const shared_ptr& p)
+ {
+ return !p;
+ }
+
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n, shared);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p, shared);
+ }
+ };
+}
+
+#endif // ODB_SHARED_PTR_TRAITS_HXX
diff --git a/odb/shared-ptr.hxx b/odb/shared-ptr.hxx
new file mode 100644
index 0000000..ee56792
--- /dev/null
+++ b/odb/shared-ptr.hxx
@@ -0,0 +1,145 @@
+// file : odb/shared-ptr.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SHARED_PTR_HXX
+#define ODB_SHARED_PTR_HXX
+
+#include <odb/shared-ptr/base.hxx>
+
+namespace odb
+{
+ template <typename X>
+ class shared_ptr: bits::counter_ops<typename bits::counter_type<X>::r, X>
+ {
+ typedef bits::counter_ops<typename bits::counter_type<X>::r, X> base;
+
+ public:
+ ~shared_ptr ()
+ {
+ if (x_ != 0)
+ base::dec (x_);
+ }
+
+ explicit
+ shared_ptr (X* x = 0)
+ : base (x), x_ (x)
+ {
+ }
+
+ shared_ptr (shared_ptr const& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ template <typename Y>
+ shared_ptr (shared_ptr<Y> const& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ shared_ptr&
+ operator= (shared_ptr const& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ template <typename Y>
+ shared_ptr&
+ operator= (shared_ptr<Y> const& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ public:
+ X*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ X&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ // Conversion to bool.
+ //
+ typedef void (shared_ptr::*boolean_convertible)();
+ void true_value () {};
+
+ operator boolean_convertible () const
+ {
+ return x_ ? &shared_ptr<X>::true_value : 0;
+ }
+
+ public:
+ X*
+ get () const
+ {
+ return x_;
+ }
+
+ X*
+ release ()
+ {
+ X* r (x_);
+ x_ = 0;
+ return r;
+ }
+
+ void
+ reset (X* x)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ base::reset (x);
+ x_ = x;
+ }
+
+ std::size_t
+ count () const
+ {
+ return x_ != 0 ? base::count (x_) : 0;
+ }
+
+ private:
+ template <typename>
+ friend class shared_ptr;
+
+ X* x_;
+ };
+}
+
+#endif // ODB_SHARED_PTR_HXX
diff --git a/odb/shared-ptr/base.cxx b/odb/shared-ptr/base.cxx
new file mode 100644
index 0000000..11fa1c2
--- /dev/null
+++ b/odb/shared-ptr/base.cxx
@@ -0,0 +1,63 @@
+// file : odb/shared-ptr/base.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/shared-ptr/base.hxx>
+
+using std::size_t;
+
+//
+//
+odb::share shared = odb::share (1);
+odb::share exclusive = odb::share (2);
+
+//
+//
+namespace odb
+{
+ char const* not_shared::
+ what () const throw ()
+ {
+ return "object is not shared";
+ }
+}
+
+//
+//
+void*
+operator new (size_t n, odb::share s) throw (std::bad_alloc)
+{
+ if (s == shared)
+ {
+ // Here we need to make sure we don't break the alignment of the
+ // returned block. For that we need to know the maximum alignment
+ // of this platform. Twice the pointer size is a good guess for
+ // most platforms.
+ //
+ size_t* p = static_cast<size_t*> (operator new (n + 2 * sizeof (size_t)));
+ *p++ = 1; // Initial count.
+ *p++ = 0xDEADBEEF; // Signature.
+ return p;
+ }
+ else
+ return operator new (n);
+
+}
+
+void
+operator delete (void* p, odb::share s) throw ()
+{
+ // This version of operator delete is only called when the c-tor
+ // fails. In this case there is no object and we can just free the
+ // memory.
+ //
+ if (s == shared)
+ {
+ size_t* sp = static_cast<size_t*> (p);
+ sp -= 2;
+ operator delete (sp);
+ }
+ else
+ operator delete (p);
+}
diff --git a/odb/shared-ptr/base.hxx b/odb/shared-ptr/base.hxx
new file mode 100644
index 0000000..8c1b62b
--- /dev/null
+++ b/odb/shared-ptr/base.hxx
@@ -0,0 +1,91 @@
+// file : odb/shared-ptr/base.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SHARED_PTR_BASE_HXX
+#define ODB_SHARED_PTR_BASE_HXX
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/exception.hxx>
+
+namespace odb
+{
+ struct share
+ {
+ explicit
+ share (char id);
+
+ bool
+ operator== (share) const;
+
+ private:
+ char id_;
+ };
+}
+
+extern odb::share shared;
+extern odb::share exclusive;
+
+void*
+operator new (std::size_t, odb::share) throw (std::bad_alloc);
+
+void
+operator delete (void*, odb::share) throw ();
+
+namespace odb
+{
+ struct not_shared: exception
+ {
+ virtual char const*
+ what () const throw ();
+ };
+
+ struct shared_base
+ {
+ shared_base ();
+ shared_base (shared_base const&);
+ shared_base&
+ operator= (shared_base const&);
+
+ void
+ _inc_ref ();
+
+ bool
+ _dec_ref ();
+
+ std::size_t
+ _ref_count () const;
+
+ void*
+ operator new (std::size_t, share) throw (std::bad_alloc);
+
+ void
+ operator delete (void*, share) throw ();
+
+ void
+ operator delete (void*) throw ();
+
+ protected:
+ std::size_t counter_;
+ };
+
+ template <typename X>
+ inline X*
+ inc_ref (X*);
+
+ template <typename X>
+ inline void
+ dec_ref (X*);
+
+ template <typename X>
+ inline std::size_t
+ ref_count (X const*);
+}
+
+#include <odb/shared-ptr/base.ixx>
+#include <odb/shared-ptr/base.txx>
+
+#endif // ODB_SHARED_PTR_BASE_HXX
diff --git a/odb/shared-ptr/base.ixx b/odb/shared-ptr/base.ixx
new file mode 100644
index 0000000..992e156
--- /dev/null
+++ b/odb/shared-ptr/base.ixx
@@ -0,0 +1,79 @@
+// file : odb/shared-ptr/base.ixx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ // share
+ //
+
+ inline share::
+ share (char id)
+ : id_ (id)
+ {
+ }
+
+ inline bool share::
+ operator== (share x) const
+ {
+ return id_ == x.id_;
+ }
+
+ // shared_base
+ //
+
+ inline shared_base::
+ shared_base ()
+ : counter_ (1)
+ {
+ }
+
+ inline shared_base::
+ shared_base (shared_base const&)
+ : counter_ (1)
+ {
+ }
+
+ inline shared_base& shared_base::
+ operator= (shared_base const&)
+ {
+ return *this;
+ }
+
+ inline void shared_base::
+ _inc_ref ()
+ {
+ counter_++;
+ }
+
+ inline bool shared_base::
+ _dec_ref ()
+ {
+ return --counter_ == 0;
+ }
+
+ inline std::size_t shared_base::
+ _ref_count () const
+ {
+ return counter_;
+ }
+
+ inline void* shared_base::
+ operator new (std::size_t n, share) throw (std::bad_alloc)
+ {
+ return ::operator new (n);
+ }
+
+ inline void shared_base::
+ operator delete (void* p, share) throw ()
+ {
+ ::operator delete (p);
+ }
+
+ inline void shared_base::
+ operator delete (void* p) throw ()
+ {
+ ::operator delete (p);
+ }
+}
diff --git a/odb/shared-ptr/base.txx b/odb/shared-ptr/base.txx
new file mode 100644
index 0000000..a8d5f26
--- /dev/null
+++ b/odb/shared-ptr/base.txx
@@ -0,0 +1,183 @@
+// file : odb/shared-ptr/base.txx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/meta/answer.hxx>
+#include <odb/meta/polymorphic-p.hxx>
+
+namespace odb
+{
+ namespace bits
+ {
+ // Support for locating the counter in the memory block.
+ //
+ template <typename X, bool poly = meta::polymorphic_p<X>::r>
+ struct locator;
+
+ template <typename X>
+ struct locator<X, false>
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ std::size_t* p (reinterpret_cast<std::size_t*> (x));
+
+ if (*(--p) != 0xDEADBEEF)
+ throw not_shared ();
+
+ return --p;
+ }
+ };
+
+ template <typename X>
+ struct locator<X, true>
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ std::size_t* p (
+ static_cast<std::size_t*> (
+ dynamic_cast<void*> (x)));
+
+ if (*(--p) != 0xDEADBEEF)
+ throw not_shared ();
+
+ return --p;
+ }
+ };
+
+ template <typename X>
+ std::size_t*
+ counter (X const* p)
+ {
+ return bits::locator<X>::counter (const_cast<X*> (p));
+ }
+
+ // Counter type and operations.
+ //
+ meta::no test (...);
+ meta::yes test (shared_base*);
+
+ template <typename X,
+ std::size_t A = sizeof (bits::test (reinterpret_cast<X*> (0)))>
+ struct counter_type;
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::no)>
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::yes)>
+ {
+ typedef shared_base r;
+ };
+
+ template <typename X, typename Y>
+ struct counter_ops;
+
+ template <typename X>
+ struct counter_ops<X, X>
+ {
+ counter_ops (X const* p) : counter_ (p ? bits::counter (p) : 0) {}
+ counter_ops (counter_ops const& x) : counter_ (x.counter_) {}
+
+ template <typename Y>
+ counter_ops (counter_ops<Y, Y> const& x) : counter_ (x.counter_) {}
+
+ counter_ops&
+ operator= (counter_ops const& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ template <typename Y>
+ counter_ops&
+ operator= (counter_ops<Y, Y> const& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ void
+ reset (X const* p)
+ {
+ counter_ = p ? bits::counter (p) : 0;
+ }
+
+ void
+ inc (X*)
+ {
+ (*counter_)++;
+ }
+
+ void
+ dec (X* p)
+ {
+ if (--(*counter_) == 0)
+ {
+ p->~X ();
+ operator delete (counter_); // Counter is the top of the memory block.
+ }
+ }
+
+ std::size_t
+ count (X const*) const
+ {
+ return *counter_;
+ }
+
+ std::size_t* counter_;
+ };
+
+ template <typename Y>
+ struct counter_ops<shared_base, Y>
+ {
+ counter_ops (Y const*) {}
+
+ void
+ reset (Y const*) {}
+
+ void
+ inc (shared_base* p) {p->_inc_ref ();}
+
+ void
+ dec (Y* p)
+ {
+ if (static_cast<shared_base*> (p)->_dec_ref ())
+ delete p;
+ }
+
+ std::size_t
+ count (shared_base const* p) const {return p->_ref_count ();}
+ };
+ }
+
+ template <typename X>
+ inline X*
+ inc_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.inc (p);
+ return p;
+ }
+
+ template <typename X>
+ inline void
+ dec_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.dec (p);
+ }
+
+ template <typename X>
+ inline std::size_t
+ ref_count (X const* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ return c.count (p);
+ }
+}
diff --git a/odb/traits.hxx b/odb/traits.hxx
new file mode 100644
index 0000000..e8e109c
--- /dev/null
+++ b/odb/traits.hxx
@@ -0,0 +1,93 @@
+// file : odb/traits.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRAITS_HXX
+#define ODB_TRAITS_HXX
+
+#include <memory> // std::auto_ptr
+
+#include <odb/forward.hxx>
+#include <odb/shared-ptr.hxx>
+#include <odb/shared-ptr-traits.hxx>
+
+namespace odb
+{
+ enum id_source
+ {
+ ids_assigned /* Assigned by the application. */
+ };
+
+ // Specializations should defined the following members:
+ //
+ // id_type - object id (primary key) type
+ // id_source - object id (primary key) source
+ // id_type id (const T&) - get object id
+ //
+ // void insert (database&, const T&)
+ // void update (database&, const T&)
+ // void erase (database&, const id_type&)
+ // memory_traits<T>::shared_ptr find (database&, const id_type&)
+ //
+ // And inherit from object_memory and object_factory.
+ //
+ // template <typename T>
+ // class access::object_traits;
+
+ template <typename T>
+ class access::object_memory
+ {
+ public:
+ typedef odb::shared_ptr<T> shared_ptr;
+ typedef std::auto_ptr<T> unique_ptr;
+ };
+
+ template <typename T>
+ class access::object_factory
+ {
+ public:
+ static typename object_memory<T>::shared_ptr
+ create ()
+ {
+ // By default use shared_ptr-specific construction.
+ //
+ return shared_factory<typename object_memory<T>::shared_ptr>::create ();
+ }
+ };
+
+ template <typename P>
+ class access::shared_factory
+ {
+ public:
+ typedef typename shared_ptr_traits<P>::type object_type;
+
+ static P
+ create ()
+ {
+ void* v (shared_ptr_traits<P>::allocate (sizeof (object_type)));
+ guard g (v);
+ P p (new (v) object_type);
+ g.release ();
+ return p;
+ }
+ private:
+ struct guard
+ {
+ guard (void* p): p_ (p) {}
+ ~guard () {if (p_) shared_ptr_traits<P>::free (p_);}
+ void release () {p_ = 0;}
+ void* p_;
+ };
+ };
+
+ template <typename T>
+ struct object_traits: access::object_traits<T>
+ {
+ typedef
+ shared_ptr_traits<typename access::object_traits<T>::shared_ptr>
+ shared_ops;
+ };
+}
+
+#endif // ODB_TRAITS_HXX
diff --git a/odb/transaction.cxx b/odb/transaction.cxx
new file mode 100644
index 0000000..7b28ea2
--- /dev/null
+++ b/odb/transaction.cxx
@@ -0,0 +1,93 @@
+// file : odb/transaction.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb
+{
+ //
+ // transaction
+ //
+
+ static transaction* current_transaction = 0;
+
+ transaction::
+ transaction (transaction_impl* impl)
+ : finilized_ (false), impl_ (impl)
+ {
+ current_transaction = this;
+ }
+
+ transaction::
+ ~transaction ()
+ {
+ if (!finilized_)
+ {
+ try
+ {
+ rollback ();
+ }
+ catch (const database_exception&)
+ {
+ // Ignore it.
+ }
+ }
+
+ current_transaction = 0;
+ delete impl_;
+ }
+
+ bool transaction::
+ has_current ()
+ {
+ return current_transaction != 0;
+ }
+
+ transaction& transaction::
+ current ()
+ {
+ if (current_transaction == 0)
+ throw not_in_transaction ();
+
+ return *current_transaction;
+ }
+
+ void transaction::
+ commit ()
+ {
+ if (finilized_)
+ throw transaction_already_finilized ();
+
+ // Flush the session if we are in the session-per-transaction mode.
+ //
+ if (impl_->own_session ())
+ session ().flush ();
+
+ impl_->commit ();
+ finilized_ = true;
+ }
+
+ void transaction::
+ rollback ()
+ {
+ if (finilized_)
+ throw transaction_already_finilized ();
+
+ finilized_ = true;
+ impl_->rollback ();
+ }
+
+ //
+ // transaction_impl
+ //
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ if (own_session ())
+ reinterpret_cast<session_type*> (&session_mem_)->~session ();
+ }
+}
diff --git a/odb/transaction.hxx b/odb/transaction.hxx
new file mode 100644
index 0000000..01d4a38
--- /dev/null
+++ b/odb/transaction.hxx
@@ -0,0 +1,139 @@
+// file : odb/transaction.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRANSACTION_HXX
+#define ODB_TRANSACTION_HXX
+
+#include <new> // placement new
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class transaction
+ {
+ public:
+ typedef odb::session session_type;
+ typedef odb::database database_type;
+
+ explicit
+ transaction (transaction_impl*);
+
+ // Unless the transaction has been already been finilized (explicitly
+ // committed or rolled back), the destructor will roll it back.
+ //
+ ~transaction ();
+
+ void
+ commit ();
+
+ void
+ rollback ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the session this transaction is part of.
+ //
+ session_type&
+ session ();
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Return true if there is a transaction in effect.
+ //
+ static bool
+ has_current ();
+
+ public:
+ transaction_impl&
+ implementation ();
+
+ private:
+ // Copying or assignment of transactions is not supported.
+ //
+ transaction (const transaction&);
+ transaction& operator= (const transaction&);
+
+ protected:
+ bool finilized_;
+ transaction_impl* impl_;
+ };
+}
+
+#include <odb/session.hxx>
+
+namespace odb
+{
+ class transaction_impl
+ {
+ protected:
+ friend class transaction;
+
+ typedef odb::session session_type;
+ typedef odb::database database_type;
+
+ transaction_impl (database_type& db, session_type& s)
+ : database_ (db), session_ (s)
+ {
+ }
+
+ transaction_impl (database_type& db)
+ : database_ (db),
+ session_ (*reinterpret_cast<session_type*> (&session_mem_))
+ {
+ new (&session_mem_) session_type ();
+ }
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ commit () = 0;
+
+ virtual void
+ rollback () = 0;
+
+ session_type&
+ session ()
+ {
+ return session_;
+ }
+
+ database_type&
+ database ()
+ {
+ return database_;
+ }
+
+ bool
+ own_session () const
+ {
+ return &session_ == reinterpret_cast<const session_type*> (&session_mem_);
+ }
+
+ protected:
+ database_type& database_;
+ session_type& session_;
+
+ union
+ {
+ void* align_;
+ char data_[sizeof (session_type)];
+ } session_mem_;
+ };
+}
+
+#include <odb/transaction.ixx>
+
+#endif // ODB_TRANSACTION_HXX
diff --git a/odb/transaction.ixx b/odb/transaction.ixx
new file mode 100644
index 0000000..e6247e6
--- /dev/null
+++ b/odb/transaction.ixx
@@ -0,0 +1,25 @@
+// file : odb/transaction.ixx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return impl_->database ();
+ }
+
+ inline transaction::session_type& transaction::
+ session ()
+ {
+ return impl_->session ();
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ return *impl_;
+ }
+}