aboutsummaryrefslogtreecommitdiff
path: root/libcutl
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-12-16 20:29:05 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-02-24 16:40:04 +0300
commit8e761289a2446367267c6c0d9a26e734f0f78306 (patch)
treefb495d8c18801f271d124ee48731f10df396ca89 /libcutl
parent4c8104756b92b9fa16b3a725e8a6aa620dfd606e (diff)
Get rid of legacy build systems and rename cutl/ to libcutl/
Diffstat (limited to 'libcutl')
-rw-r--r--libcutl/buildfile47
-rw-r--r--libcutl/compiler/code-stream.hxx152
-rw-r--r--libcutl/compiler/code-stream.txx93
-rw-r--r--libcutl/compiler/context.cxx53
-rw-r--r--libcutl/compiler/context.hxx136
-rw-r--r--libcutl/compiler/context.txx87
-rw-r--r--libcutl/compiler/cxx-indenter.cxx48
-rw-r--r--libcutl/compiler/cxx-indenter.hxx170
-rw-r--r--libcutl/compiler/cxx-indenter.ixx68
-rw-r--r--libcutl/compiler/cxx-indenter.txx816
-rw-r--r--libcutl/compiler/sloc-counter.hxx77
-rw-r--r--libcutl/compiler/sloc-counter.txx223
-rw-r--r--libcutl/compiler/traversal.hxx170
-rw-r--r--libcutl/compiler/traversal.txx143
-rw-r--r--libcutl/compiler/type-id.hxx45
-rw-r--r--libcutl/compiler/type-id.ixx43
-rw-r--r--libcutl/compiler/type-id.txx16
-rw-r--r--libcutl/compiler/type-info.cxx29
-rw-r--r--libcutl/compiler/type-info.hxx110
-rw-r--r--libcutl/compiler/type-info.ixx94
-rw-r--r--libcutl/container/any.hxx149
-rw-r--r--libcutl/container/graph.hxx214
-rw-r--r--libcutl/container/graph.txx348
-rw-r--r--libcutl/container/key.hxx70
-rw-r--r--libcutl/container/map-iterator.hxx68
-rw-r--r--libcutl/container/multi-index.hxx14
-rw-r--r--libcutl/container/pointer-iterator.hxx125
-rw-r--r--libcutl/exception.cxx15
-rw-r--r--libcutl/exception.hxx22
-rw-r--r--libcutl/export.hxx40
-rw-r--r--libcutl/fs/auto-remove.cxx30
-rw-r--r--libcutl/fs/auto-remove.hxx82
-rw-r--r--libcutl/fs/exception.cxx16
-rw-r--r--libcutl/fs/exception.hxx36
-rw-r--r--libcutl/fs/path.cxx120
-rw-r--r--libcutl/fs/path.hxx332
-rw-r--r--libcutl/fs/path.ixx70
-rw-r--r--libcutl/fs/path.txx209
-rw-r--r--libcutl/meta/answer.hxx23
-rw-r--r--libcutl/meta/class-p.hxx26
-rw-r--r--libcutl/meta/polymorphic-p.hxx49
-rw-r--r--libcutl/meta/remove-c.hxx25
-rw-r--r--libcutl/meta/remove-cv.hxx22
-rw-r--r--libcutl/meta/remove-p.hxx25
-rw-r--r--libcutl/meta/remove-v.hxx25
-rw-r--r--libcutl/re.hxx280
-rw-r--r--libcutl/re/re.cxx462
-rw-r--r--libcutl/re/re.txx68
-rw-r--r--libcutl/shared-ptr.hxx157
-rw-r--r--libcutl/shared-ptr/base.cxx61
-rw-r--r--libcutl/shared-ptr/base.hxx91
-rw-r--r--libcutl/shared-ptr/base.ixx77
-rw-r--r--libcutl/shared-ptr/base.txx198
-rw-r--r--libcutl/static-ptr.hxx73
-rw-r--r--libcutl/version.hxx0
-rw-r--r--libcutl/version.hxx.in38
56 files changed, 6280 insertions, 0 deletions
diff --git a/libcutl/buildfile b/libcutl/buildfile
new file mode 100644
index 0000000..f3fe4dc
--- /dev/null
+++ b/libcutl/buildfile
@@ -0,0 +1,47 @@
+# file : libcutl/buildfile
+# license : MIT; see accompanying LICENSE file
+
+lib{cutl}: {hxx ixx txx cxx}{** -version} {hxx}{version}
+
+# Include the generated version header into the distribution (so that we don't
+# pick up an installed one) and don't remove it when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+hxx{version}: in{version} $src_root/manifest
+hxx{version}:
+{
+ dist = true
+ clean = ($src_root != $out_root)
+}
+
+# Build options.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBCUTL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBCUTL_SHARED_BUILD
+
+# Export options.
+#
+lib{cutl}: cxx.export.poptions = "-I$out_root" "-I$src_root"
+
+liba{cutl}: cxx.export.poptions += -DLIBCUTL_STATIC
+libs{cutl}: cxx.export.poptions += -DLIBCUTL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{cutl}: bin.lib.version = @"-$version.project_id"
+else
+ lib{cutl}: bin.lib.version = @"-$version.major.$version.minor"
+
+# Install into the libcutl/ subdirectory of, say, /usr/include/ recreating
+# subdirectories.
+#
+{hxx ixx txx}{*}:
+{
+ install = include/libcutl/
+ install.subdirs = true
+}
diff --git a/libcutl/compiler/code-stream.hxx b/libcutl/compiler/code-stream.hxx
new file mode 100644
index 0000000..e5fe996
--- /dev/null
+++ b/libcutl/compiler/code-stream.hxx
@@ -0,0 +1,152 @@
+// file : libcutl/compiler/code-stream.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_CODE_STREAM_HXX
+#define LIBCUTL_COMPILER_CODE_STREAM_HXX
+
+#include <ostream>
+
+#include <libcutl/exception.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ //
+ //
+ template <typename C>
+ class code_stream
+ {
+ public:
+ code_stream () {}
+
+ virtual
+ ~code_stream ();
+
+ public:
+ virtual void
+ put (C) = 0;
+
+ // Unbuffer flushes internal formatting buffers (if any).
+ // Note that unbuffer is not exactly flushing since it can
+ // result in formatting errors and in general can not be
+ // called at arbitrary points. Natural use case would be
+ // to call unbuffer at the end of the stream when no more
+ // data is expected.
+ //
+ virtual void
+ unbuffer () = 0;
+
+ private:
+ code_stream (code_stream const&);
+
+ code_stream&
+ operator= (code_stream const&);
+ };
+
+ //
+ //
+ template <typename C>
+ class from_streambuf_adapter: public code_stream<C>
+ {
+ public:
+ typedef typename std::basic_streambuf<C>::traits_type traits_type;
+ typedef typename std::basic_streambuf<C>::int_type int_type;
+
+ class eof: exception {};
+ class sync: exception {};
+
+ public:
+ from_streambuf_adapter (std::basic_streambuf<C>& stream)
+ : stream_ (stream)
+ {
+ }
+
+ private:
+ from_streambuf_adapter (from_streambuf_adapter const&);
+
+ from_streambuf_adapter&
+ operator= (from_streambuf_adapter const&);
+
+ public:
+ virtual void
+ put (C c);
+
+ virtual void
+ unbuffer ();
+
+ private:
+ std::basic_streambuf<C>& stream_;
+ };
+
+ //
+ //
+ template <typename C>
+ class to_streambuf_adapter: public std::basic_streambuf<C>
+ {
+ public:
+ typedef typename std::basic_streambuf<C>::traits_type traits_type;
+ typedef typename std::basic_streambuf<C>::int_type int_type;
+
+ public:
+ to_streambuf_adapter (code_stream<C>& stream)
+ : stream_ (stream)
+ {
+ }
+
+ private:
+ to_streambuf_adapter (to_streambuf_adapter const&);
+
+ to_streambuf_adapter&
+ operator= (to_streambuf_adapter const&);
+
+ public:
+ virtual int_type
+ overflow (int_type i);
+
+ // Does nothing since calling unbuffer here would be dangerous.
+ // See the note in code_stream.
+ //
+ virtual int
+ sync ();
+
+ private:
+ code_stream<C>& stream_;
+ };
+
+ //
+ //
+ template <template <typename> class S, typename C>
+ class ostream_filter
+ {
+ public:
+ typedef S<C> stream_type;
+
+ ostream_filter (std::basic_ostream<C>& os);
+ ~ostream_filter () /*noexcept (false)*/;
+
+ stream_type&
+ stream ()
+ {
+ return stream_;
+ }
+
+ private:
+ ostream_filter (ostream_filter const&);
+
+ ostream_filter&
+ operator= (ostream_filter const&);
+
+ private:
+ std::basic_ostream<C>& os_;
+ std::basic_streambuf<C>* prev_;
+ from_streambuf_adapter<C> from_adapter_;
+ stream_type stream_;
+ to_streambuf_adapter<C> to_adapter_;
+ };
+ }
+}
+
+#include <libcutl/compiler/code-stream.txx>
+
+#endif // LIBCUTL_COMPILER_CODE_STREAM_HXX
diff --git a/libcutl/compiler/code-stream.txx b/libcutl/compiler/code-stream.txx
new file mode 100644
index 0000000..15fc351
--- /dev/null
+++ b/libcutl/compiler/code-stream.txx
@@ -0,0 +1,93 @@
+// file : libcutl/compiler/code-stream.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ // code_stream
+ //
+
+ template <typename C>
+ code_stream<C>::~code_stream ()
+ {
+ }
+
+ // from_streambuf_adapter
+ //
+
+ template <typename C>
+ void from_streambuf_adapter<C>::
+ put (C c)
+ {
+ int_type i (stream_.sputc (c));
+
+ if (i == traits_type::eof ())
+ throw eof ();
+ }
+
+ template <typename C>
+ void from_streambuf_adapter<C>::
+ unbuffer ()
+ {
+ if (stream_.pubsync () != 0)
+ throw sync ();
+ }
+
+ // to_streambuf_adapter
+ //
+
+ template <typename C>
+ typename to_streambuf_adapter<C>::int_type to_streambuf_adapter<C>::
+ overflow (int_type i)
+ {
+ try
+ {
+ stream_.put (traits_type::to_char_type (i));
+ return i;
+ }
+ catch (typename from_streambuf_adapter<C>::eof const&)
+ {
+ return traits_type::eof ();
+ }
+ }
+
+ template <typename C>
+ int to_streambuf_adapter<C>::
+ sync ()
+ {
+ return 0;
+ }
+
+ // ostream_filter
+ //
+
+ template <template <typename> class S, typename C>
+ ostream_filter<S, C>::
+ ostream_filter (std::basic_ostream<C>& os)
+ : os_ (os),
+ prev_ (os_.rdbuf ()),
+ from_adapter_ (*prev_),
+ stream_ (from_adapter_),
+ to_adapter_ (stream_)
+ {
+ os_.rdbuf (&to_adapter_);
+ }
+
+ template <template <typename> class S, typename C>
+ ostream_filter<S, C>::
+ ~ostream_filter ()
+ {
+ try
+ {
+ stream_.unbuffer ();
+ }
+ catch (...)
+ {
+ os_.rdbuf (prev_);
+ }
+
+ os_.rdbuf (prev_);
+ }
+ }
+}
diff --git a/libcutl/compiler/context.cxx b/libcutl/compiler/context.cxx
new file mode 100644
index 0000000..9a24e65
--- /dev/null
+++ b/libcutl/compiler/context.cxx
@@ -0,0 +1,53 @@
+// file : libcutl/compiler/context.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/compiler/context.hxx>
+
+using namespace std;
+
+namespace cutl
+{
+ namespace compiler
+ {
+ void context::
+ set (string const& key, container::any const& value)
+ {
+ using container::any;
+
+ std::pair<map::iterator, bool> r (
+ map_.insert (map::value_type (key, value)));
+
+ any& x (r.first->second);
+
+ if (!r.second)
+ {
+ if (value.type_info () != x.type_info ())
+ throw typing ();
+
+ x = value;
+ }
+ }
+
+ void context::
+ remove (string const& key)
+ {
+ map::iterator i (map_.find (key));
+
+ if (i == map_.end ())
+ throw no_entry ();
+
+ map_.erase (i);
+ }
+
+ type_info const& context::
+ type_info (string const& key) const
+ {
+ map::const_iterator i (map_.find (key));
+
+ if (i == map_.end ())
+ throw no_entry ();
+
+ return i->second.type_info ();
+ }
+ }
+}
diff --git a/libcutl/compiler/context.hxx b/libcutl/compiler/context.hxx
new file mode 100644
index 0000000..fc120cc
--- /dev/null
+++ b/libcutl/compiler/context.hxx
@@ -0,0 +1,136 @@
+// file : libcutl/compiler/context.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_CONTEXT_HXX
+#define LIBCUTL_COMPILER_CONTEXT_HXX
+
+#include <map>
+#include <string>
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <libcutl/exception.hxx>
+#include <libcutl/container/any.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ class LIBCUTL_EXPORT context
+ {
+ public:
+ struct no_entry: exception {};
+ struct typing: exception {};
+
+ public:
+ context () {}
+
+ void
+ swap (context& c)
+ {
+ map_.swap (c.map_);
+ }
+
+ private:
+ context (context const&);
+
+ context&
+ operator= (context const&);
+
+ public:
+ std::size_t
+ count (char const* key) const
+ {
+ return count (std::string (key));
+ }
+
+ std::size_t
+ count (std::string const& key) const
+ {
+ return map_.count (key);
+ }
+
+ template <typename X>
+ X&
+ get (char const* key)
+ {
+ return get<X> (std::string (key));
+ }
+
+ template <typename X>
+ X&
+ get (std::string const& key);
+
+ template <typename X>
+ X const&
+ get (char const* key) const
+ {
+ return get<X> (std::string (key));
+ }
+
+ template <typename X>
+ X const&
+ get (std::string const& key) const;
+
+ template <typename X>
+ X const&
+ get (char const* key, X const& default_value) const
+ {
+ return get<X> (std::string (key), default_value);
+ }
+
+ template <typename X>
+ X const&
+ get (std::string const& key, X const& default_value) const;
+
+ template <typename X>
+ X&
+ set (char const* key, X const& value)
+ {
+ return set<X> (std::string (key), value);
+ }
+
+ template <typename X>
+ X&
+ set (std::string const& key, X const& value);
+
+ void
+ set (char const* key, container::any const& value)
+ {
+ return set (std::string (key), value);
+ }
+
+ void
+ set (std::string const& key, container::any const& value);
+
+ void
+ remove (char const* key)
+ {
+ remove (std::string (key));
+ }
+
+ void
+ remove (std::string const& key);
+
+ std::type_info const&
+ type_info (char const* key) const
+ {
+ return type_info (std::string (key));
+ }
+
+ std::type_info const&
+ type_info (std::string const& key) const;
+
+ private:
+ typedef std::map<std::string, container::any> map;
+
+ map map_;
+ };
+ }
+}
+
+#include <libcutl/compiler/context.txx>
+
+#endif // LIBCUTL_COMPILER_CONTEXT_HXX
diff --git a/libcutl/compiler/context.txx b/libcutl/compiler/context.txx
new file mode 100644
index 0000000..1d292d8
--- /dev/null
+++ b/libcutl/compiler/context.txx
@@ -0,0 +1,87 @@
+// file : libcutl/compiler/context.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename X>
+ X& context::
+ get (std::string const& key)
+ {
+ map::iterator i (map_.find (key));
+
+ if (i == map_.end ())
+ throw no_entry ();
+
+ try
+ {
+ return i->second. template value<X> ();
+ }
+ catch (container::any::typing const&)
+ {
+ throw typing ();
+ }
+ }
+
+ template <typename X>
+ X const& context::
+ get (std::string const& key) const
+ {
+ map::const_iterator i (map_.find (key));
+
+ if (i == map_.end ())
+ throw no_entry ();
+
+ try
+ {
+ return i->second. template value<X> ();
+ }
+ catch (container::any::typing const&)
+ {
+ throw typing ();
+ }
+ }
+
+ template <typename X>
+ X const& context::
+ get (std::string const& key, X const& default_value) const
+ {
+ map::const_iterator i (map_.find (key));
+
+ if (i == map_.end ())
+ return default_value;
+
+ try
+ {
+ return i->second. template value<X> ();
+ }
+ catch (container::any::typing const&)
+ {
+ throw typing ();
+ }
+ }
+
+ template <typename X>
+ X& context::
+ set (std::string const& key, X const& value)
+ {
+ try
+ {
+ std::pair<map::iterator, bool> r (
+ map_.insert (map::value_type (key, value)));
+
+ X& x (r.first->second. template value<X> ());
+
+ if (!r.second)
+ x = value;
+
+ return x;
+ }
+ catch (container::any::typing const&)
+ {
+ throw typing ();
+ }
+ }
+ }
+}
diff --git a/libcutl/compiler/cxx-indenter.cxx b/libcutl/compiler/cxx-indenter.cxx
new file mode 100644
index 0000000..72bcaf5
--- /dev/null
+++ b/libcutl/compiler/cxx-indenter.cxx
@@ -0,0 +1,48 @@
+// file : libcutl/compiler/cxx-indenter.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/compiler/cxx-indenter.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template<>
+ LIBCUTL_EXPORT char const* cxx_indenter<char>::
+ keyword (cxx_indenter<char>::keyword_type t)
+ {
+ static char const* keywords[] =
+ {
+ "if",
+ "do",
+ "for",
+ "else",
+ "case",
+ "while",
+ "catch",
+ "default"
+ };
+
+ return keywords[t];
+ }
+
+ template<>
+ LIBCUTL_EXPORT wchar_t const* cxx_indenter<wchar_t>::
+ keyword (cxx_indenter<wchar_t>::keyword_type t)
+ {
+ static wchar_t const* keywords[] =
+ {
+ L"if",
+ L"do",
+ L"for",
+ L"else",
+ L"case",
+ L"while",
+ L"catch",
+ L"default"
+ };
+
+ return keywords[t];
+ }
+ }
+}
diff --git a/libcutl/compiler/cxx-indenter.hxx b/libcutl/compiler/cxx-indenter.hxx
new file mode 100644
index 0000000..81a838f
--- /dev/null
+++ b/libcutl/compiler/cxx-indenter.hxx
@@ -0,0 +1,170 @@
+// file : libcutl/compiler/cxx-indenter.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_CXX_INDENTER_HXX
+#define LIBCUTL_COMPILER_CXX_INDENTER_HXX
+
+#include <set>
+#include <stack>
+#include <deque>
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <libcutl/compiler/code-stream.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ class cxx_indenter: public code_stream<C>
+ {
+ public:
+ cxx_indenter (code_stream<C>& out);
+
+ private:
+ cxx_indenter (cxx_indenter const&);
+
+ cxx_indenter&
+ operator= (cxx_indenter const&);
+
+ public:
+ virtual void
+ put (C);
+
+ virtual void
+ unbuffer ();
+
+ private:
+ typedef std::basic_string<C> string;
+
+ enum construct
+ {
+ con_other,
+ con_pp_dir,
+ con_c_com,
+ con_cxx_com,
+ con_string_lit,
+ con_char_lit
+ };
+
+ private:
+ void
+ next_token (string const& old, C);
+
+ void
+ ensure_new_line ();
+
+ void
+ output_indentation ();
+
+ void
+ write (C);
+
+ private:
+ void
+ tokenize (C, construct old);
+
+ void
+ retire (C);
+
+ private:
+ enum char_class_type
+ {
+ cc_alpha, // Alpha + '_'.
+ cc_digit,
+ cc_op_punc, // Operator or punctuation.
+ cc_space
+ };
+
+ static char_class_type
+ char_class (C);
+
+ private:
+ enum keyword_type
+ {
+ kw_if,
+ kw_do,
+ kw_for,
+ kw_else,
+ kw_case,
+ kw_while,
+ kw_catch,
+ kw_default
+ };
+
+ static C const*
+ keyword (keyword_type);
+
+ private:
+ code_stream<C>& out_;
+ bool buffering_; // True if write() should buffer the char.
+ std::size_t position_; // Current position on the line.
+ std::size_t paren_balance_; // ( ) balance.
+ std::stack<std::size_t> indentation_;
+ std::size_t spaces_;
+ bool suppress_nl_;
+ construct construct_;
+
+ // Special state stack for the do-while construct. The presence
+ // of an element in the stack indicates that we are in a braced
+ // do-while construct. The value of the element is the brace
+ // balance.
+ std::stack<std::size_t> do_while_state_;
+
+ typedef std::deque<C> hold;
+ hold hold_;
+
+ private:
+ string token_; // previously fully recognized token
+ string lexeme_; // current lexeme (accumulator)
+
+ // Keywords that may be folowed by a single-line block, e.g., if,
+ // else, etc.
+ //
+ std::set<string> single_line_blocks_;
+
+ // Keywords that may follow (and be related) to a previous block,
+ // e.g., else, case, catch.
+ //
+ std::set<string> follow_blocks_;
+
+ string do_;
+ string lbrace_;
+ string rbrace_;
+
+ private:
+ // Single-line indented blocks such as if, else, while, etc. The
+ // newline flag indicates whether a new line has been seen after
+ // the keyword. This is needed to properly distinguish cases such
+ // as:
+ //
+ // else if (...)
+ // foo ();
+ //
+ // else
+ // if (...)
+ // foo ();
+ //
+ struct indent_block
+ {
+ indent_block (bool newline, std::size_t indentation)
+ : newline_ (newline), indentation_ (indentation)
+ {
+ }
+
+ bool newline_;
+ std::size_t indentation_; // Size of the indentation_ stack
+ // corresponding to this block, or
+ // 0 if it is not indented.
+ };
+
+ std::stack<indent_block> indent_stack_;
+ };
+ }
+}
+
+#include <libcutl/compiler/cxx-indenter.ixx>
+#include <libcutl/compiler/cxx-indenter.txx>
+
+#endif // LIBCUTL_COMPILER_CXX_INDENTER_HXX
diff --git a/libcutl/compiler/cxx-indenter.ixx b/libcutl/compiler/cxx-indenter.ixx
new file mode 100644
index 0000000..5f59fd0
--- /dev/null
+++ b/libcutl/compiler/cxx-indenter.ixx
@@ -0,0 +1,68 @@
+// file : libcutl/compiler/cxx-indenter.ixx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ inline typename cxx_indenter<C>::char_class_type cxx_indenter<C>::
+ char_class (C c)
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return cc_digit;
+
+ case '!':
+ case '%':
+ case '^':
+ case '&':
+ case '*':
+ case '(':
+ case ')':
+ case '-':
+ case '+':
+ case '=':
+ case '{':
+ case '}':
+ case '|':
+ case '~':
+ case '[':
+ case ']':
+ case '\\':
+ case ';':
+ case '\'':
+ case ':':
+ case '"':
+ case '<':
+ case '>':
+ case '?':
+ case ',':
+ case '.':
+ case '/':
+ return cc_op_punc;
+
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\f':
+ case '\r':
+ case '\v':
+ return cc_space;
+
+ default:
+ return cc_alpha;
+ }
+ }
+ }
+}
diff --git a/libcutl/compiler/cxx-indenter.txx b/libcutl/compiler/cxx-indenter.txx
new file mode 100644
index 0000000..d1af9b2
--- /dev/null
+++ b/libcutl/compiler/cxx-indenter.txx
@@ -0,0 +1,816 @@
+// file : libcutl/compiler/cxx-indenter.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ cxx_indenter<C>::
+ cxx_indenter (code_stream<C>& out)
+ : out_ (out),
+ buffering_ (false),
+ position_ (0),
+ paren_balance_ (0),
+ spaces_ (2),
+ construct_ (con_other),
+ do_ (keyword(kw_do)),
+ lbrace_ (1, '{'),
+ rbrace_ (1, '}')
+ {
+ indentation_.push (0);
+
+ single_line_blocks_.insert (keyword(kw_if));
+ single_line_blocks_.insert (keyword(kw_do));
+ single_line_blocks_.insert (keyword(kw_for));
+ single_line_blocks_.insert (keyword(kw_else));
+ single_line_blocks_.insert (keyword(kw_case));
+ single_line_blocks_.insert (keyword(kw_while));
+
+ follow_blocks_.insert (keyword(kw_else));
+ follow_blocks_.insert (keyword(kw_case));
+ follow_blocks_.insert (keyword(kw_catch));
+ follow_blocks_.insert (keyword(kw_default));
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ put (C c)
+ {
+ // First determine what kind of construct we are in.
+ //
+ construct new_con (construct_);
+ construct old_con (construct_);
+
+ switch (c)
+ {
+ case '\n':
+ {
+ if (construct_ == con_pp_dir ||
+ construct_ == con_cxx_com)
+ construct_ = new_con = con_other;
+
+ break;
+ }
+ case '#':
+ {
+ if (construct_ == con_other)
+ construct_ = new_con = con_pp_dir;
+
+ break;
+ }
+ case '\"':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com &&
+ construct_ != con_char_lit)
+ {
+ // We might be in an escape sequence.
+ //
+ bool es (!hold_.empty () && hold_.back () == '\\');
+
+ if (es)
+ {
+ // Scan the hold sequence backwards to figure out if this
+ // backslash is part of this escape sequence or a preceding
+ // one.
+ //
+ for (typename hold::reverse_iterator i (hold_.rbegin () + 1),
+ e (hold_.rend ()); i != e && *i == '\\'; ++i)
+ es = !es;
+ }
+
+ if (!es)
+ {
+ if (construct_ == con_string_lit)
+ new_con = con_other;
+ else
+ construct_ = new_con = con_string_lit;
+ }
+ }
+
+ break;
+ }
+ case '\'':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com &&
+ construct_ != con_string_lit)
+ {
+ // We might be in an escape sequence.
+ //
+ bool es (!hold_.empty () && hold_.back () == '\\');
+
+ if (es)
+ {
+ // Scan the hold sequence backwards to figure out if this
+ // backslash is part of this escape sequence or a preceding
+ // one.
+ //
+ for (typename hold::reverse_iterator i (hold_.rbegin () + 1),
+ e (hold_.rend ()); i != e && *i == '\\'; ++i)
+ es = !es;
+ }
+
+ if (!es)
+ {
+ if (construct_ == con_char_lit)
+ new_con = con_other;
+ else
+ construct_ = new_con = con_char_lit;
+ }
+ }
+
+ break;
+ }
+ case '/':
+ {
+ if (construct_ == con_other)
+ {
+ if (!hold_.empty () && hold_.back () == '/')
+ construct_ = new_con = con_cxx_com;
+ }
+
+ if (construct_ == con_c_com)
+ {
+ if (!hold_.empty () && hold_.back () == '*')
+ construct_ = new_con = con_other;
+ }
+
+ break;
+ }
+ case '*':
+ {
+ if (construct_ == con_other)
+ {
+ if (!hold_.empty () && hold_.back () == '/')
+ construct_ = new_con = con_c_com;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Special handling of CPP directives.
+ //
+ if (construct_ == con_pp_dir)
+ {
+ write (c);
+ position_++;
+ return;
+ }
+
+ //
+ //
+ tokenize (c, old_con);
+
+
+ // Indentation in parenthesis. We don't need to make sure
+ // we are not in a comments, etc., because we make sure we
+ // don't hold anything in those states.
+ //
+ if (!hold_.empty () && hold_.back () == '(')
+ {
+ unbuffer (); // We don't need to hold it anymore.
+
+ if (c == '\n')
+ indentation_.push (indentation_.top () + spaces_);
+ else
+ indentation_.push (position_);
+ }
+
+
+ //
+ //
+ bool defaulting (false);
+
+ switch (c)
+ {
+ case '\n':
+ {
+ if (!indent_stack_.empty () && construct_ == con_other)
+ indent_stack_.top ().newline_ = true;
+
+ hold_.push_back (c);
+ position_ = 0; // Starting a new line.
+
+ break;
+ }
+ case '{':
+ {
+ if (construct_ == con_other)
+ {
+ if (!indent_stack_.empty ())
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Pop the indented block and one level of indentation.
+ //
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+
+ ensure_new_line ();
+ output_indentation ();
+ write (c);
+ ensure_new_line ();
+
+ indentation_.push (indentation_.top () + spaces_);
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case '}':
+ {
+ if (construct_ == con_other)
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ // Reduce multiple newlines to one.
+ //
+ while (hold_.size () > 1)
+ {
+ typename hold::reverse_iterator i (hold_.rbegin ());
+
+ if (*i == '\n' && *(i + 1) == '\n')
+ hold_.pop_back ();
+ else
+ break;
+ }
+
+ ensure_new_line ();
+ output_indentation ();
+
+ hold_.push_back (c);
+
+ // Add double newline after '}'.
+ //
+ hold_.push_back ('\n');
+ hold_.push_back ('\n');
+ position_ = 0;
+
+ if (!indent_stack_.empty ())
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Now pop all the indented blocks while also popping the
+ // indentation stack. Do it only if the indentation match.
+ // If it doesn't then that means this inden_stack entry is
+ // for some other, outer block.
+ //
+ while (!indent_stack_.empty () &&
+ indent_stack_.top ().indentation_ ==
+ indentation_.size ())
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+ }
+
+ buffering_ = true;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ';':
+ {
+ if (construct_ == con_other)
+ {
+ // for (;;)
+ //
+ if (!indent_stack_.empty () && paren_balance_ == 0)
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Now pop all the indented blocks while also popping the
+ // indentation stack. Do it only if the indentation match.
+ // If they don't then it means we are inside a block and
+ // the stack should be popped after seeing '}', not ';'.
+ //
+ while (!indent_stack_.empty () &&
+ indent_stack_.top ().indentation_ ==
+ indentation_.size ())
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+ }
+
+ if (paren_balance_ != 0)
+ {
+ // We are inside for (;;) statement. Nothing to do here.
+ //
+ defaulting = true;
+ }
+ else
+ {
+ // Handling '};' case.
+ //
+
+ bool brace (false);
+
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ brace = pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+
+ output_indentation ();
+ write (c);
+ position_++;
+
+ if (brace)
+ {
+ hold_.push_back ('\n');
+ hold_.push_back ('\n');
+ }
+
+ ensure_new_line ();
+ }
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ',':
+ {
+ if (construct_ == con_other)
+ {
+ // Handling '},' case.
+ //
+
+ bool brace (false);
+
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ brace = pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+
+ output_indentation ();
+ write (c);
+ position_++;
+
+ if (brace)
+ hold_.push_back ('\n');
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ' ':
+ {
+ if (construct_ == con_other)
+ {
+ // Handling '} foo_;' case.
+ //
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+ }
+
+ defaulting = true;
+ break;
+ }
+ case '\\':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+
+ }
+ case '(':
+ {
+ if (construct_ == con_other)
+ {
+ // Hold it so that we can see what's coming next.
+ //
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ paren_balance_++;
+ }
+ else
+ defaulting = true;
+ break;
+ }
+ case ')':
+ {
+ if (construct_ == con_other)
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ if (paren_balance_ > 0)
+ paren_balance_--;
+ }
+
+ defaulting = true;
+ break;
+ }
+ case '/':
+ {
+ if (construct_ == con_other)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case '*':
+ {
+ if (construct_ == con_c_com)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ default:
+ {
+ defaulting = true;
+ break;
+ }
+ }
+
+ if (defaulting)
+ {
+ output_indentation ();
+ write (c);
+ position_++;
+ }
+
+ construct_ = new_con;
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ unbuffer ()
+ {
+ for (; !hold_.empty (); hold_.pop_front ())
+ out_.put (hold_.front ());
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ next_token (string const& old, C c)
+ {
+ // Handle one line indentation blocks (if, else, etc).
+ //
+ if (single_line_blocks_.find (token_) != single_line_blocks_.end ())
+ {
+ // Only indent sub-blocks if we are on a new line.
+ //
+ bool indent (indent_stack_.empty () ||
+ indent_stack_.top ().newline_);
+
+ if (indent)
+ indentation_.push (indentation_.top () + spaces_);
+
+ indent_stack_.push (
+ indent_block (c == '\n', (indent ? indentation_.size () : 0)));
+ }
+
+ // Keep track of the do ... while construct in order to suppress
+ // the newline after } and before while.
+ //
+ if (old == do_ && token_ == lbrace_)
+ do_while_state_.push (0);
+
+ if (!do_while_state_.empty ())
+ {
+ if (token_ == lbrace_)
+ do_while_state_.top ()++;
+
+ if (token_ == rbrace_)
+ do_while_state_.top ()--;
+ }
+
+ // Suppress double newline in the "}else", etc., cases.
+ //
+ if (old == rbrace_)
+ {
+ bool dw (!do_while_state_.empty () && do_while_state_.top () == 0);
+
+ if (follow_blocks_.find (token_) != follow_blocks_.end () || dw)
+ {
+ if (dw)
+ do_while_state_.pop ();
+
+ // Reduce double newline after "}" into a single one.
+ //
+ typename hold::iterator i (hold_.end ()), b (hold_.begin ());
+
+ for (--i; i != b; --i)
+ {
+ // See if this is the end of the "}\n\n" sequence.
+ //
+ if (*i == '\n')
+ {
+ --i;
+ if (i != b && *i == '\n')
+ {
+ --i;
+ if (*i == '}')
+ {
+ ++i;
+ hold_.erase (i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Stop buffering unless we have another closing brace.
+ //
+ if (token_ != rbrace_)
+ buffering_ = false;
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ ensure_new_line ()
+ {
+ if (hold_.empty () || hold_.back () != '\n')
+ {
+ hold_.push_back ('\n');
+ position_ = 0; // Starting a new line.
+ }
+ }
+
+
+ template <typename C>
+ void cxx_indenter<C>::
+ output_indentation ()
+ {
+ if (!hold_.empty () && hold_.back () == '\n')
+ {
+ for (std::size_t i (0); i < indentation_.top (); ++i)
+ write (' ');
+
+ position_ += indentation_.top ();
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ write (C c)
+ {
+ hold_.push_back (c);
+
+ if (!buffering_)
+ {
+ for (; !hold_.empty (); hold_.pop_front ())
+ out_.put (hold_.front ());
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ tokenize (C c, construct old)
+ {
+ //
+ //
+ switch (construct_)
+ {
+ case con_pp_dir:
+ {
+ if (old == con_other) // Start PP directive
+ retire (c);
+
+ return;
+ }
+ case con_c_com:
+ {
+ if (old == con_other) // Start C comment.
+ lexeme_.clear ();
+
+ return;
+ }
+ case con_cxx_com:
+ {
+ if (old == con_other) // Start C++ comment.
+ lexeme_.clear ();
+
+ return;
+ }
+ case con_string_lit:
+ {
+ if (old == con_other) // Start string literal
+ retire (c);
+
+ lexeme_ += c;
+ return;
+ }
+ case con_char_lit:
+ {
+ if (old == con_other) // Start char literal
+ retire (c);
+
+ lexeme_ += c;
+ return;
+ }
+ default:
+ break;
+ }
+
+ // construct_ == other
+ //
+ switch (old)
+ {
+ case con_pp_dir:
+ {
+ // End PP directive (newline).
+ //
+ return;
+ }
+ case con_c_com:
+ {
+ // End C comment.
+ //
+ return;
+ }
+ case con_cxx_com:
+ {
+ // End C++ comment (newline).
+ //
+ return;
+ }
+ case con_string_lit:
+ {
+ // End string literal (").
+ //
+ lexeme_ += c;
+ return;
+ }
+ case con_char_lit:
+ {
+ // End char literal (').
+ //
+ lexeme_ += c;
+ return;
+ }
+ default:
+ break;
+ }
+
+
+ // construct_ == old == other
+ //
+
+ switch (char_class (c))
+ {
+ case cc_alpha:
+ {
+ if (lexeme_.empty () ||
+ char_class (lexeme_[0]) == cc_alpha)
+ lexeme_ += c;
+ else
+ {
+ retire (c);
+ lexeme_ += c;
+ }
+ break;
+ }
+ case cc_digit:
+ {
+ if (lexeme_.empty ())
+ lexeme_ += c;
+ else
+ {
+ char_class_type cc (char_class (lexeme_[0]));
+
+ if (cc == cc_alpha || cc == cc_digit)
+ lexeme_ += c;
+ else
+ {
+ retire (c);
+ lexeme_ += c;
+ }
+ }
+ break;
+ }
+ case cc_op_punc:
+ {
+ retire (c);
+ lexeme_ += c;
+ break;
+ }
+ case cc_space:
+ {
+ retire (c);
+ break;
+ }
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ retire (C c)
+ {
+ if (!lexeme_.empty ())
+ {
+ token_.swap (lexeme_);
+ next_token (lexeme_, c);
+ lexeme_.clear ();
+ }
+ }
+ }
+}
diff --git a/libcutl/compiler/sloc-counter.hxx b/libcutl/compiler/sloc-counter.hxx
new file mode 100644
index 0000000..581b6f3
--- /dev/null
+++ b/libcutl/compiler/sloc-counter.hxx
@@ -0,0 +1,77 @@
+// file : libcutl/compiler/sloc-counter.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_SLOC_COUNTER_HXX
+#define LIBCUTL_COMPILER_SLOC_COUNTER_HXX
+
+#include <cstddef> // std::size_t
+
+#include <libcutl/compiler/code-stream.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ class sloc_counter: public code_stream<C>
+ {
+ public:
+ sloc_counter (code_stream<C>& out);
+
+ std::size_t
+ count () const
+ {
+ return count_;
+ }
+
+ private:
+ sloc_counter (sloc_counter const&);
+
+ sloc_counter&
+ operator= (sloc_counter const&);
+
+ public:
+ virtual void
+ put (C);
+
+ virtual void
+ unbuffer ();
+
+ private:
+ void
+ code (C);
+
+ void
+ c_comment (C);
+
+ void
+ cxx_comment (C);
+
+ void
+ char_literal (C);
+
+ void
+ string_literal (C);
+
+ private:
+ code_stream<C>& out_;
+ std::size_t count_;
+
+ C prev_; // Previous character or '\0'.
+ bool code_counted_; // This code line has already been counted.
+
+ enum construct
+ {
+ con_code,
+ con_c_com,
+ con_cxx_com,
+ con_char_lit,
+ con_string_lit
+ } construct_;
+ };
+ }
+}
+
+#include <libcutl/compiler/sloc-counter.txx>
+
+#endif // LIBCUTL_COMPILER_SLOC_COUNTER_HXX
diff --git a/libcutl/compiler/sloc-counter.txx b/libcutl/compiler/sloc-counter.txx
new file mode 100644
index 0000000..be622d9
--- /dev/null
+++ b/libcutl/compiler/sloc-counter.txx
@@ -0,0 +1,223 @@
+// file : libcutl/compiler/sloc-counter.txx
+// license : MIT; see accompanying LICENSE file
+
+#include <cctype> // std::isspace
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ sloc_counter<C>::
+ sloc_counter (code_stream<C>& out)
+ : out_ (out),
+ count_ (0),
+ prev_ ('\0'),
+ code_counted_ (false),
+ construct_ (con_code)
+ {
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ put (C c)
+ {
+ construct old (construct_);
+
+ switch (construct_)
+ {
+ case con_code:
+ {
+ code (c);
+ break;
+ }
+ case con_c_com:
+ {
+ c_comment (c);
+ break;
+ }
+ case con_cxx_com:
+ {
+ cxx_comment (c);
+ break;
+ }
+ case con_char_lit:
+ {
+ char_literal (c);
+ break;
+ }
+ case con_string_lit:
+ {
+ string_literal (c);
+ break;
+ }
+ }
+
+ // There are cases when a previous character has been already
+ // 'used' and therefore can not be used again. Good example
+ // would be '/* *//'. Here, the second slash doesn't start
+ // C++ comment since it was already used by C comment.
+ //
+ // To account for this we are going to set prev_ to '\0' when
+ // the mode changes.
+ //
+
+ prev_ = (old == construct_) ? c : '\0';
+
+ out_.put (c);
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ unbuffer ()
+ {
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ code (C c)
+ {
+ bool count (true);
+
+ switch (c)
+ {
+ case '/':
+ {
+ if (prev_ == '/')
+ {
+ construct_ = con_cxx_com;
+ count = false;
+ }
+ else
+ {
+ // This slash can be a beginning of a comment but we
+ // yet have no way to know. Will have to examine it later
+ // (see below).
+ //
+ count = false;
+ }
+
+ break;
+ }
+ case '*':
+ {
+ if (prev_ == '/')
+ {
+ construct_ = con_c_com;
+ count = false;
+ }
+ break;
+ }
+ case '\'':
+ {
+ construct_ = con_char_lit;
+ break;
+ }
+ case '"':
+ {
+ construct_ = con_string_lit;
+ break;
+ }
+ case '\n':
+ {
+ code_counted_ = false; // Reset for a new line.
+ count = false;
+ break;
+ }
+ default:
+ {
+ if (std::isspace (c))
+ count = false;
+ break;
+ }
+ }
+
+ if (!code_counted_)
+ {
+ if (count)
+ {
+ count_++;
+ code_counted_ = true;
+ }
+ else if (prev_ == '/' && construct_ == con_code)
+ {
+ // This condition accounts for the fact that we cannot count
+ // '/' right away since it can be a beginning of a comment.
+ //
+ count_++;
+ code_counted_ = (c != '\n');
+ }
+ }
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ c_comment (C c)
+ {
+ switch (c)
+ {
+ case '/':
+ {
+ if (prev_ == '*')
+ construct_ = con_code;
+ break;
+ }
+ case '\n':
+ {
+ code_counted_ = false; // Reset for a new line.
+ break;
+ }
+ }
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ cxx_comment (C c)
+ {
+ switch (c)
+ {
+ case '\n':
+ {
+ construct_ = con_code;
+ code_counted_ = false; // Reset for a new line.
+ break;
+ }
+ }
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ char_literal (C c)
+ {
+ switch (c)
+ {
+ case '\'':
+ {
+ if (prev_ != '\\')
+ construct_ = con_code;
+ break;
+ }
+ }
+ }
+
+ template <typename C>
+ void sloc_counter<C>::
+ string_literal (C c)
+ {
+ switch (c)
+ {
+ case '"':
+ {
+ if (prev_ != '\\')
+ construct_ = con_code;
+ break;
+ }
+ case '\n':
+ {
+ count_++;
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libcutl/compiler/traversal.hxx b/libcutl/compiler/traversal.hxx
new file mode 100644
index 0000000..dffb22d
--- /dev/null
+++ b/libcutl/compiler/traversal.hxx
@@ -0,0 +1,170 @@
+// file : libcutl/compiler/traversal.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_TRAVERSAL_HXX
+#define LIBCUTL_COMPILER_TRAVERSAL_HXX
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include <libcutl/compiler/type-info.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ //
+ //
+ template<typename B>
+ class traverser
+ {
+ public:
+ virtual
+ ~traverser ();
+
+ virtual void
+ trampoline (B&) = 0;
+ };
+
+ //
+ //
+ template<typename B>
+ class traverser_map
+ {
+ public:
+ typedef std::vector<traverser<B>*> traversers;
+
+ struct map_type: std::map<type_id, traversers>
+ {
+ map_type () {}
+
+ // Don't copy traverser maps. We do it here instead of in
+ // traverser_map to pacify GCC's -Wextra insisting we must
+ // explicitly initialize virtual bases in copy constructor.
+ //
+ map_type (map_type const&): std::map<type_id, traversers> () {}
+ map_type& operator= (map_type const&) {return *this;}
+ };
+
+ typedef typename map_type::const_iterator iterator;
+
+ iterator
+ begin () const
+ {
+ return map_.begin ();
+ }
+
+ iterator
+ end () const
+ {
+ return map_.end ();
+ }
+
+ void
+ add (type_id const& id, traverser<B>& t)
+ {
+ traversers& travs (map_[id]);
+ travs.push_back (&t);
+ }
+
+ protected:
+ map_type map_;
+ };
+
+ //
+ //
+ template <typename X, typename B>
+ class traverser_impl: public traverser<B>,
+ public virtual traverser_map<B>
+ {
+ public:
+ typedef X type;
+
+ traverser_impl ()
+ {
+ this->add (typeid (type), *this);
+ }
+
+ traverser_impl (traverser_impl const&)
+ {
+ this->add (typeid (type), *this);
+ }
+
+ virtual void
+ traverse (type&) = 0;
+
+ public:
+ virtual void
+ trampoline (B&);
+ };
+
+ //
+ //
+ template <typename B>
+ class dispatcher: public virtual traverser_map<B>
+ {
+ public:
+ virtual
+ ~dispatcher ();
+
+ void
+ traverser (traverser_map<B>&);
+
+ virtual void
+ dispatch (B&);
+
+ public:
+ template <typename I, typename X>
+ static void
+ iterate_and_dispatch (I begin, I end, dispatcher<X>& d)
+ {
+ for (; begin != end; ++begin)
+ {
+ d.dispatch (*begin);
+ }
+ }
+
+ template <typename T, typename A, typename I, typename X>
+ static void
+ iterate_and_dispatch (I begin,
+ I end,
+ dispatcher<X>& d,
+ T& t,
+ void (T::*next)(A&),
+ A& a)
+ {
+ for (; begin != end;)
+ {
+ d.dispatch (*begin);
+
+ if (++begin != end && next != 0)
+ (t.*next) (a);
+ }
+ }
+
+ private:
+ struct comparator
+ {
+ bool
+ operator () (type_info const& a, type_info const& b) const
+ {
+ return a.type_id () < b.type_id ();
+ }
+ };
+
+ typedef std::map<type_info, std::size_t, comparator> level_map;
+ typedef std::set<type_info, comparator> type_info_set;
+
+ static std::size_t
+ compute_levels (type_info const&, std::size_t current, level_map&);
+
+ static void
+ flatten_tree (type_info const&, type_info_set&);
+ };
+ }
+}
+
+#include <libcutl/compiler/traversal.txx>
+
+#endif // LIBCUTL_COMPILER_TRAVERSAL_HXX
diff --git a/libcutl/compiler/traversal.txx b/libcutl/compiler/traversal.txx
new file mode 100644
index 0000000..67e7f4d
--- /dev/null
+++ b/libcutl/compiler/traversal.txx
@@ -0,0 +1,143 @@
+// file : libcutl/compiler/traversal.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ // traverser
+ //
+ template<typename B>
+ traverser<B>::
+ ~traverser ()
+ {
+ }
+
+ // traverser_impl
+ //
+
+ template <typename X, typename B>
+ void traverser_impl<X, B>::
+ trampoline (B& x)
+ {
+ this->traverse (dynamic_cast<type&> (x));
+ }
+
+ // dispatcher
+ //
+
+ template <typename B>
+ dispatcher<B>::
+ ~dispatcher ()
+ {
+ }
+
+ template <typename B>
+ void dispatcher<B>::
+ traverser (traverser_map<B>& m)
+ {
+ // Copy entries from m to our map.
+ //
+ for (typename traverser_map<B>::iterator
+ i (m.begin ()), e (m.end ()); i != e; ++i)
+ {
+ typename traverser_map<B>::traversers& travs (this->map_[i->first]);
+
+ for (typename traverser_map<B>::traversers::const_iterator
+ t (i->second.begin ()), e (i->second.end ()); t != e; ++t)
+ {
+ travs.push_back (*t);
+ }
+ }
+ }
+
+ template <typename B>
+ void dispatcher<B>::
+ dispatch (B& x)
+ {
+ using std::size_t;
+
+ level_map levels;
+ type_info const& ti (lookup (x));
+ size_t max (compute_levels (ti, 0, levels));
+
+ // cerr << "starting dispatch process for " << ti.type_id ().name ()
+ // << " with " << max << " levels" << endl;
+
+ for (size_t l (0); l < max + 1; ++l)
+ {
+ type_info_set dispatched;
+
+ for (typename level_map::const_iterator
+ i (levels.begin ()), e (levels.end ()); i != e; ++i)
+ {
+ if (i->second == l)
+ {
+ typename traverser_map<B>::map_type::const_iterator v (
+ this->map_.find (i->first.type_id ()));
+
+ if (v != this->map_.end ())
+ {
+ // cerr << "dispatching traversers for " << ti.type_id ().name ()
+ // << " as " << i->first.type_id ().name () << endl;
+
+ typename traverser_map<B>::traversers const& travs (v->second);
+
+ for (typename traverser_map<B>::traversers::const_iterator
+ ti (travs.begin ()), te (travs.end ()); ti != te; ++ti)
+ {
+ (*ti)->trampoline (x);
+ }
+
+ flatten_tree (i->first, dispatched);
+ }
+ }
+ }
+
+ // Remove traversed types from the level map.
+ //
+ for (typename type_info_set::const_iterator i (dispatched.begin ());
+ i != dispatched.end (); ++i)
+ {
+ levels.erase (*i);
+ }
+ }
+ }
+
+ template <typename B>
+ std::size_t dispatcher<B>::
+ compute_levels (type_info const& ti, std::size_t cur, level_map& map)
+ {
+ using std::size_t;
+
+ size_t ret (cur);
+
+ if (map.find (ti) == map.end () || map[ti] < cur)
+ map[ti] = cur;
+
+ for (type_info::base_iterator i (ti.begin_base ());
+ i != ti.end_base (); ++i)
+ {
+ size_t tmp (compute_levels (i->type_info (), cur + 1, map));
+
+ if (tmp > ret)
+ ret = tmp;
+ }
+
+ return ret;
+ }
+
+ template <typename B>
+ void dispatcher<B>::
+ flatten_tree (type_info const& ti, type_info_set& set)
+ {
+ set.insert (ti);
+
+ for (type_info::base_iterator i (ti.begin_base ());
+ i != ti.end_base (); ++i)
+ {
+ flatten_tree (i->type_info (), set);
+ }
+ }
+ }
+}
diff --git a/libcutl/compiler/type-id.hxx b/libcutl/compiler/type-id.hxx
new file mode 100644
index 0000000..d27597e
--- /dev/null
+++ b/libcutl/compiler/type-id.hxx
@@ -0,0 +1,45 @@
+// file : libcutl/compiler/type-id.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_TYPE_ID_HXX
+#define LIBCUTL_COMPILER_TYPE_ID_HXX
+
+#include <typeinfo> // std::type_info
+
+namespace cutl
+{
+ namespace compiler
+ {
+ class type_id
+ {
+ public:
+ template<typename X>
+ type_id (X const volatile&);
+
+ type_id (std::type_info const&);
+
+ public:
+ char const*
+ name () const;
+
+ friend bool
+ operator== (type_id const&, type_id const&);
+
+ friend bool
+ operator!= (type_id const&, type_id const&);
+
+ friend bool
+ operator< (type_id const&, type_id const&);
+
+ private:
+ std::type_info const* ti_;
+ };
+
+ typedef type_id type_id_t;
+ }
+}
+
+#include <libcutl/compiler/type-id.ixx>
+#include <libcutl/compiler/type-id.txx>
+
+#endif // LIBCUTL_COMPILER_TYPE_ID_HXX
diff --git a/libcutl/compiler/type-id.ixx b/libcutl/compiler/type-id.ixx
new file mode 100644
index 0000000..017e29a
--- /dev/null
+++ b/libcutl/compiler/type-id.ixx
@@ -0,0 +1,43 @@
+// file : libcutl/compiler/type-id.ixx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ inline
+ type_id::
+ type_id (std::type_info const& ti)
+ : ti_ (&ti)
+ {
+ }
+
+ inline
+ char const* type_id::
+ name () const
+ {
+ return ti_->name ();
+ }
+
+ inline
+ bool
+ operator== (type_id const& x, type_id const& y)
+ {
+ return *x.ti_ == *y.ti_;
+ }
+
+ inline
+ bool
+ operator!= (type_id const& x, type_id const& y)
+ {
+ return *x.ti_ != *y.ti_;
+ }
+
+ inline
+ bool
+ operator< (type_id const& x, type_id const& y)
+ {
+ return x.ti_->before (*y.ti_);
+ }
+ }
+}
diff --git a/libcutl/compiler/type-id.txx b/libcutl/compiler/type-id.txx
new file mode 100644
index 0000000..e23e4a5
--- /dev/null
+++ b/libcutl/compiler/type-id.txx
@@ -0,0 +1,16 @@
+// file : libcutl/compiler/type-id.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename X>
+ inline
+ type_id::
+ type_id (X const volatile& x)
+ : ti_ (&typeid (x))
+ {
+ }
+ }
+}
diff --git a/libcutl/compiler/type-info.cxx b/libcutl/compiler/type-info.cxx
new file mode 100644
index 0000000..cefd451
--- /dev/null
+++ b/libcutl/compiler/type-info.cxx
@@ -0,0 +1,29 @@
+// file : libcutl/compiler/type-info.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ using namespace bits;
+
+ type_info const&
+ lookup (type_id const& tid)
+ {
+ type_info_map::const_iterator i (type_info_map_->find (tid));
+
+ if (i == type_info_map_->end ())
+ throw no_type_info ();
+
+ return i->second;
+ }
+
+ void
+ insert (type_info const& ti)
+ {
+ type_info_map_->insert (type_info_map::value_type (ti.type_id (), ti));
+ }
+ }
+}
diff --git a/libcutl/compiler/type-info.hxx b/libcutl/compiler/type-info.hxx
new file mode 100644
index 0000000..4263a41
--- /dev/null
+++ b/libcutl/compiler/type-info.hxx
@@ -0,0 +1,110 @@
+// file : libcutl/compiler/type-info.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_COMPILER_TYPE_INFO_HXX
+#define LIBCUTL_COMPILER_TYPE_INFO_HXX
+
+#include <map>
+#include <vector>
+#include <typeinfo> // std::type_info
+
+#include <libcutl/exception.hxx>
+#include <libcutl/static-ptr.hxx>
+#include <libcutl/compiler/type-id.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace compiler
+ {
+ //
+ //
+ class type_info;
+ typedef type_info type_info_t;
+
+
+ //
+ //
+ class base_info
+ {
+ public:
+ base_info (type_id const&);
+
+ public:
+ type_info_t const&
+ type_info () const;
+
+ private:
+ type_id type_id_;
+ mutable type_info_t const* type_info_;
+ };
+
+ typedef base_info base_info_t;
+
+
+ //
+ //
+ class type_info
+ {
+ typedef std::vector<base_info> bases;
+
+ public:
+ typedef
+ bases::const_iterator
+ base_iterator;
+
+ public:
+ type_info (type_id_t const&);
+
+ type_id_t
+ type_id () const;
+
+ base_iterator
+ begin_base () const;
+
+ base_iterator
+ end_base () const;
+
+ void
+ add_base (type_id_t const&);
+
+ private:
+ type_id_t type_id_;
+ bases bases_;
+ };
+
+
+ //
+ //
+ class no_type_info: exception {};
+
+ LIBCUTL_EXPORT type_info const&
+ lookup (type_id const&);
+
+ type_info const&
+ lookup (std::type_info const&);
+
+ template <typename X>
+ type_info const&
+ lookup (X const volatile&);
+
+ template<typename X>
+ type_info const&
+ lookup ();
+
+ LIBCUTL_EXPORT void
+ insert (type_info const&);
+
+ namespace bits
+ {
+ struct default_type_info_id {};
+ typedef std::map<type_id, type_info> type_info_map;
+ static static_ptr<type_info_map, default_type_info_id> type_info_map_;
+ }
+ }
+}
+
+#include <libcutl/compiler/type-info.ixx>
+
+#endif // LIBCUTL_COMPILER_TYPE_INFO_HXX
diff --git a/libcutl/compiler/type-info.ixx b/libcutl/compiler/type-info.ixx
new file mode 100644
index 0000000..908a7ca
--- /dev/null
+++ b/libcutl/compiler/type-info.ixx
@@ -0,0 +1,94 @@
+// file : libcutl/compiler/type-info.ixx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ // base_info
+ //
+
+ inline
+ base_info::
+ base_info (type_id const& type_id)
+ : type_id_ (type_id), type_info_ (0)
+ {
+ }
+
+ inline
+ type_info_t const& base_info::
+ type_info () const
+ {
+ // We need to do delayed lookup because of the unpredictable
+ // order in which type information may be added.
+ //
+ // @@ MT-unsafe
+ //
+ if (type_info_ == 0)
+ type_info_ = &(lookup (type_id_));
+
+ return *type_info_;
+ }
+
+ // type_info
+ //
+
+ inline
+ type_info::
+ type_info (type_id_t const& tid)
+ : type_id_ (tid)
+ {
+ }
+
+ inline
+ type_id_t type_info::
+ type_id () const
+ {
+ return type_id_;
+ }
+
+ inline
+ type_info::base_iterator type_info::
+ begin_base () const
+ {
+ return bases_.begin ();
+ }
+
+
+ inline
+ type_info::base_iterator type_info::
+ end_base () const
+ {
+ return bases_.end ();
+ }
+
+ inline
+ void type_info::
+ add_base (type_id_t const& tid)
+ {
+ bases_.push_back (base_info (tid));
+ }
+
+ //
+ //
+ inline type_info const&
+ lookup (std::type_info const& tid)
+ {
+ return lookup (type_id (tid));
+ }
+
+ template <typename X>
+ inline type_info const&
+ lookup (X const volatile& x)
+ {
+ return lookup (typeid (x));
+ }
+
+ template<typename X>
+ inline type_info const&
+ lookup ()
+ {
+ return lookup (typeid (X));
+ }
+ }
+}
diff --git a/libcutl/container/any.hxx b/libcutl/container/any.hxx
new file mode 100644
index 0000000..a7b1194
--- /dev/null
+++ b/libcutl/container/any.hxx
@@ -0,0 +1,149 @@
+// file : libcutl/container/any.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_ANY_HXX
+#define LIBCUTL_CONTAINER_ANY_HXX
+
+#include <memory> // std::unique_ptr/auto_ptr
+#include <typeinfo> // std::type_info
+
+#include <libcutl/exception.hxx>
+
+namespace cutl
+{
+ namespace container
+ {
+ class any
+ {
+ public:
+ struct typing: exception {};
+
+ public:
+ any ()
+ {
+ }
+
+ template <typename X>
+ any (X const& x)
+ : holder_ (new holder_impl<X> (x))
+ {
+ }
+
+ any (any const& x)
+ : holder_ (x.holder_->clone ())
+ {
+ }
+
+ template <typename X>
+ any&
+ operator= (X const& x)
+ {
+ holder_.reset (new holder_impl<X> (x));
+ return *this;
+ }
+
+ any&
+ operator= (any const& x)
+ {
+ holder_.reset (x.holder_->clone ());
+ return *this;
+ }
+
+ public:
+ template <typename X>
+ X&
+ value ()
+ {
+ if (holder_impl<X>* p = dynamic_cast<holder_impl<X>*> (holder_.get ()))
+ return p->value ();
+ else
+ throw typing ();
+ }
+
+ template <typename X>
+ X const&
+ value () const
+ {
+ if (holder_impl<X>* p = dynamic_cast<holder_impl<X>*> (holder_.get ()))
+ return p->value ();
+ else
+ throw typing ();
+ }
+
+ std::type_info const&
+ type_info () const
+ {
+ return holder_->type_info ();
+ }
+
+ public:
+ bool
+ empty () const
+ {
+ return holder_.get () == 0;
+ }
+
+ void
+ reset ()
+ {
+ return holder_.reset ();
+ }
+
+ private:
+ class LIBCUTL_EXPORT holder
+ {
+ public:
+ virtual
+ ~holder () {}
+
+ virtual holder*
+ clone () const = 0;
+
+ virtual std::type_info const&
+ type_info () const = 0;
+ };
+
+ template <typename X>
+ class holder_impl: public holder
+ {
+ public:
+ holder_impl (X const& x)
+ : x_ (x)
+ {
+ }
+
+ virtual holder_impl*
+ clone () const
+ {
+ return new holder_impl (x_);
+ }
+
+ virtual std::type_info const&
+ type_info () const
+ {
+ return typeid (x_);
+ }
+
+ X const&
+ value () const
+ {
+ return x_;
+ }
+
+ X&
+ value ()
+ {
+ return x_;
+ }
+
+ private:
+ X x_;
+ };
+
+ private:
+ std::unique_ptr<holder> holder_;
+ };
+ }
+}
+
+#endif // LIBCUTL_CONTAINER_ANY_HXX
diff --git a/libcutl/container/graph.hxx b/libcutl/container/graph.hxx
new file mode 100644
index 0000000..14be17f
--- /dev/null
+++ b/libcutl/container/graph.hxx
@@ -0,0 +1,214 @@
+// file : libcutl/container/graph.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_GRAPH_HXX
+#define LIBCUTL_CONTAINER_GRAPH_HXX
+
+#include <map>
+
+#include <libcutl/exception.hxx>
+#include <libcutl/shared-ptr.hxx>
+
+namespace cutl
+{
+ namespace container
+ {
+ struct no_edge: exception {};
+ struct no_node: exception {};
+
+ template <typename N, typename E>
+ class graph
+ {
+ public:
+ typedef N node_base;
+ typedef E edge_base;
+
+ public:
+ template <typename T>
+ T&
+ new_node ();
+
+ template <typename T, typename A0>
+ T&
+ new_node (A0 const&);
+
+ template <typename T, typename A0, typename A1>
+ T&
+ new_node (A0 const&, A1 const&);
+
+ template <typename T, typename A0, typename A1, typename A2>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&,
+ A5 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&,
+ A5 const&, A6 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&,
+ A5 const&, A6 const&, A7 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7, typename A8>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&,
+ A5 const&, A6 const&, A7 const&, A8 const&);
+
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7, typename A8, typename A9>
+ T&
+ new_node (A0 const&, A1 const&, A2 const&, A3 const&, A4 const&,
+ A5 const&, A6 const&, A7 const&, A8 const&, A9 const&);
+
+ // Non-const versions.
+ //
+ template <typename T, typename A0>
+ T&
+ new_node (A0&);
+
+ template <typename T, typename A0, typename A1>
+ T&
+ new_node (A0&, A1&);
+
+ template <typename T, typename A0, typename A1, typename A2>
+ T&
+ new_node (A0&, A1&, A2&);
+
+ public:
+ template <typename T, typename L, typename R>
+ T&
+ new_edge (L&, R&);
+
+ template <typename T, typename L, typename R,
+ typename A0>
+ T&
+ new_edge (L&, R&, A0 const&);
+
+ template <typename T, typename L, typename R,
+ typename A0, typename A1>
+ T&
+ new_edge (L&, R&, A0 const&, A1 const&);
+
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2>
+ T&
+ new_edge (L&, R&, A0 const&, A1 const&, A2 const&);
+
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3>
+ T&
+ new_edge (L&, R&, A0 const&, A1 const&, A2 const&, A3 const&);
+
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3,
+ typename A4>
+ T&
+ new_edge (L&, R&, A0 const&, A1 const&, A2 const&, A3 const&,
+ A4 const&);
+
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+ T&
+ new_edge (L&, R&, A0 const&, A1 const&, A2 const&, A3 const&,
+ A4 const&, A5 const&);
+
+ // Functions to reset edge's nodes.
+ //
+ public:
+ template <typename TE, typename TN>
+ void
+ reset_left_node (TE& edge, TN& node)
+ {
+ edge.set_left_node (node);
+ }
+
+ template <typename TE, typename TN>
+ void
+ reset_right_node (TE& edge, TN& node)
+ {
+ edge.set_right_node (node);
+ }
+
+ // Functions to add edges to a node.
+ //
+ public:
+ template <typename TN, typename TE>
+ void
+ add_edge_left (TN& node, TE& edge)
+ {
+ node.add_edge_left (edge);
+ }
+
+ template <typename TN, typename TE>
+ void
+ add_edge_right (TN& node, TE& edge)
+ {
+ node.add_edge_right (edge);
+ }
+
+ // Functions to delete edges and nodes. In order to delete a
+ // a node without leaving any dangling edges you need to make
+ // sure that each edge pointing to it is either deleted or reset
+ // to some other node.
+ //
+ public:
+ template <typename T, typename L, typename R>
+ void
+ delete_edge (L& left_node, R& right_node, T& edge);
+
+ void
+ delete_node (node_base& node)
+ {
+ if (nodes_.erase (&node) == 0)
+ throw no_node ();
+ }
+
+ public:
+ graph () {}
+
+ private:
+ graph (graph const&);
+
+ graph&
+ operator= (graph const&);
+
+ protected:
+ typedef shared_ptr<node_base> node_ptr;
+ typedef shared_ptr<edge_base> edge_ptr;
+
+ typedef std::map<node_base*, node_ptr> nodes;
+ typedef std::map<edge_base*, edge_ptr> edges;
+
+ nodes nodes_;
+ edges edges_;
+ };
+ }
+}
+
+#include <libcutl/container/graph.txx>
+
+#endif // CUTL_CONTAINER_GRAPH_HXX
diff --git a/libcutl/container/graph.txx b/libcutl/container/graph.txx
new file mode 100644
index 0000000..404c115
--- /dev/null
+++ b/libcutl/container/graph.txx
@@ -0,0 +1,348 @@
+// file : libcutl/container/graph.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace container
+ {
+
+ // Nodes.
+ //
+
+ template <typename N, typename E>
+ template <typename T>
+ T& graph<N, E>::
+ new_node ()
+ {
+ shared_ptr<T> node (new (shared) T);
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename A0>
+ T& graph<N, E>::
+ new_node (A0 const& a0)
+ {
+ shared_ptr<T> node (new (shared) T (a0));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2, a3));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2, a3, a4));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4, A5 const& a5)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2, a3, a4, a5));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4, A5 const& a5, A6 const& a6)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2, a3, a4, a5, a6));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4, A5 const& a5, A6 const& a6, A7 const& a7)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2, a3, a4, a5, a6, a7));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7, typename A8>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4, A5 const& a5, A6 const& a6, A7 const& a7,
+ A8 const& a8)
+ {
+ shared_ptr<T> node (
+ new (shared) T (a0, a1, a2, a3, a4, a5, a6, a7, a8));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6,
+ typename A7, typename A8, typename A9>
+ T& graph<N, E>::
+ new_node (A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4, A5 const& a5, A6 const& a6, A7 const& a7,
+ A8 const& a8, A9 const& a9)
+ {
+ shared_ptr<T> node (
+ new (shared) T (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ // Non-const versions.
+ //
+ template <typename N, typename E>
+ template <typename T, typename A0>
+ T& graph<N, E>::
+ new_node (A0& a0)
+ {
+ shared_ptr<T> node (new (shared) T (a0));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1>
+ T& graph<N, E>::
+ new_node (A0& a0, A1& a1)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename A0, typename A1, typename A2>
+ T& graph<N, E>::
+ new_node (A0& a0, A1& a1, A2& a2)
+ {
+ shared_ptr<T> node (new (shared) T (a0, a1, a2));
+ nodes_[node.get ()] = node;
+
+ return *node;
+ }
+
+ // Edges.
+ //
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R>
+ T& graph<N, E>::
+ new_edge (L& l, R& r)
+ {
+ shared_ptr<T> edge (new (shared) T);
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0)
+ {
+ shared_ptr<T> edge (new (shared) T (a0));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0, typename A1>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0, A1 const& a1)
+ {
+ shared_ptr<T> edge (new (shared) T (a0, a1));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0, A1 const& a1, A2 const& a2)
+ {
+ shared_ptr<T> edge (new (shared) T (a0, a1, a2));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0, A1 const& a1, A2 const& a2,
+ A3 const& a3)
+ {
+ shared_ptr<T> edge (new (shared) T (a0, a1, a2, a3));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3,
+ typename A4>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0, A1 const& a1, A2 const& a2,
+ A3 const& a3, A4 const& a4)
+ {
+ shared_ptr<T> edge (new (shared) T (a0, a1, a2, a3, a4));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R,
+ typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+ T& graph<N, E>::
+ new_edge (L& l, R& r, A0 const& a0, A1 const& a1, A2 const& a2,
+ A3 const& a3, A4 const& a4, A5 const& a5)
+ {
+ shared_ptr<T> edge (new (shared) T (a0, a1, a2, a3, a4, a5));
+ edges_[edge.get ()] = edge;
+
+ edge->set_left_node (l);
+ edge->set_right_node (r);
+
+ l.add_edge_left (*edge);
+ r.add_edge_right (*edge);
+
+ return *edge;
+ }
+
+
+ template <typename N, typename E>
+ template <typename T, typename L, typename R>
+ void graph<N, E>::
+ delete_edge (L& l, R& r, T& edge)
+ {
+ typename edges::iterator i (edges_.find (&edge));
+
+ if (i == edges_.end () ||
+ nodes_.find (&l) == nodes_.end () ||
+ nodes_.find (&r) == nodes_.end ())
+ throw no_edge ();
+
+ r.remove_edge_right (edge);
+ l.remove_edge_left (edge);
+
+ edge.clear_right_node (r);
+ edge.clear_left_node (l);
+
+ edges_.erase (i);
+ }
+ }
+}
diff --git a/libcutl/container/key.hxx b/libcutl/container/key.hxx
new file mode 100644
index 0000000..aee63cf
--- /dev/null
+++ b/libcutl/container/key.hxx
@@ -0,0 +1,70 @@
+// file : libcutl/container/key.hxx
+// license : MIT; see accompkeying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_KEY_HXX
+#define LIBCUTL_CONTAINER_KEY_HXX
+
+namespace cutl
+{
+ namespace container
+ {
+ // A modifiable map key wrapper that can be used to implement multi-
+ // index containers, as discussed in the following post:
+ //
+ // http://www.codesynthesis.com/~boris/blog/2012/09/11/emulating-boost-multi-index-with-std-containers/
+ //
+ template <class T1, class T2 = void, class T3 = void>
+ struct key;
+
+ template <class T1>
+ struct key<T1, void, void>
+ {
+ mutable const T1* p1;
+
+ key (): p1 (0) {}
+ key (const T1& r1): p1 (&r1) {}
+ void assign (const T1& r1) const {p1 = &r1;}
+
+ bool operator< (const key& x) const {return *p1 < *x.p1;}
+ };
+
+ template <class T1, class T2>
+ struct key<T1, T2, void>
+ {
+ mutable const T1* p1;
+ mutable const T2* p2;
+
+ key (): p1 (0), p2 (0) {}
+ key (const T1& r1, const T2& r2): p1 (&r1), p2 (&r2) {}
+ void assign (const T1& r1, const T2& r2) const {p1 = &r1; p2 = &r2;}
+
+ bool operator< (const key& x) const
+ {
+ return *p1 < *x.p1 || (!(*x.p1 < *p1) && *p2 < *x.p2);
+ }
+ };
+
+ template <class T1, class T2, class T3>
+ struct key
+ {
+ mutable const T1* p1;
+ mutable const T2* p2;
+ mutable const T3* p3;
+
+ key (): p1 (0), p2 (0), p3 (0) {}
+ key (const T1& r1, const T2& r2, const T3& r3)
+ : p1 (&r1), p2 (&r2) , p3 (&r3) {}
+ void assign (const T1& r1, const T2& r2, const T3& r3) const
+ {p1 = &r1; p2 = &r2; p3 = &r3;}
+
+ bool operator< (const key& x) const
+ {
+ return (*p1 < *x.p1 ||
+ (!(*x.p1 < *p1) && (*p2 < *x.p2 ||
+ (!(*x.p2 < *p2) && *p3 < *x.p3))));
+ }
+ };
+ }
+}
+
+#endif // LIBCUTL_CONTAINER_KEY_HXX
diff --git a/libcutl/container/map-iterator.hxx b/libcutl/container/map-iterator.hxx
new file mode 100644
index 0000000..a5b65e8
--- /dev/null
+++ b/libcutl/container/map-iterator.hxx
@@ -0,0 +1,68 @@
+// file : libcutl/container/map-iterator.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_MAP_ITERATOR_HXX
+#define LIBCUTL_CONTAINER_MAP_ITERATOR_HXX
+
+namespace cutl
+{
+ namespace container
+ {
+ // Map iterator adapter that can be used to implement multi-index
+ // containers, as discussed in the following post:
+ //
+ // http://www.codesynthesis.com/~boris/blog/2012/09/11/emulating-boost-multi-index-with-std-containers/
+ //
+ template <typename M>
+ struct map_iterator: M::iterator
+ {
+ typedef typename M::iterator base_iterator;
+ typedef typename M::value_type::second_type value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+
+ map_iterator () {}
+ map_iterator (base_iterator i): base_iterator (i) {}
+
+ reference
+ operator* () const
+ {
+ return base_iterator::operator* ().second;
+ }
+
+ pointer
+ operator-> () const
+ {
+ return &base_iterator::operator-> ()->second;
+ }
+ };
+
+ template <typename M>
+ struct map_const_iterator: M::const_iterator
+ {
+ typedef typename M::iterator base_iterator;
+ typedef typename M::const_iterator base_const_iterator;
+ typedef const typename M::value_type::second_type value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+
+ map_const_iterator () {}
+ map_const_iterator (base_iterator i): base_const_iterator (i) {}
+ map_const_iterator (base_const_iterator i): base_const_iterator (i) {}
+
+ reference
+ operator* () const
+ {
+ return base_const_iterator::operator* ().second;
+ }
+
+ pointer
+ operator-> () const
+ {
+ return &base_const_iterator::operator-> ()->second;
+ }
+ };
+ }
+}
+
+#endif // LIBCUTL_CONTAINER_MAP_ITERATOR_HXX
diff --git a/libcutl/container/multi-index.hxx b/libcutl/container/multi-index.hxx
new file mode 100644
index 0000000..455b57c
--- /dev/null
+++ b/libcutl/container/multi-index.hxx
@@ -0,0 +1,14 @@
+// file : libcutl/container/multi-index.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_MULTI_INDEX_HXX
+#define LIBCUTL_CONTAINER_MULTI_INDEX_HXX
+
+// Multi-index containers support. See the following post for details:
+//
+// http://www.codesynthesis.com/~boris/blog/2012/09/11/emulating-boost-multi-index-with-std-containers/
+//
+#include <libcutl/container/key.hxx>
+#include <libcutl/container/map-iterator.hxx>
+
+#endif // LIBCUTL_CONTAINER_MULTI_INDEX_HXX
diff --git a/libcutl/container/pointer-iterator.hxx b/libcutl/container/pointer-iterator.hxx
new file mode 100644
index 0000000..586e15b
--- /dev/null
+++ b/libcutl/container/pointer-iterator.hxx
@@ -0,0 +1,125 @@
+// file : libcutl/container/pointer-iterator.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_CONTAINER_POINTER_ITERATOR_HXX
+#define LIBCUTL_CONTAINER_POINTER_ITERATOR_HXX
+
+#include <iterator> // std::iterator_traits
+
+#include <libcutl/meta/remove-p.hxx>
+
+namespace cutl
+{
+ namespace container
+ {
+ template <typename I>
+ class pointer_iterator
+ {
+ public:
+ typedef
+ typename meta::remove_p<typename std::iterator_traits<I>::value_type>::r
+ value_type;
+
+ typedef
+ typename std::iterator_traits<I>::iterator_category
+ iterator_category;
+
+ typedef
+ typename std::iterator_traits<I>::difference_type
+ difference_type;
+
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef I base_iterator;
+
+ public:
+ pointer_iterator ()
+ : i_ () // I can be of a pointer type.
+ {
+ }
+
+ pointer_iterator (I const& i)
+ : i_ (i)
+ {
+ }
+
+ public:
+ reference
+ operator* () const
+ {
+ return **i_;
+ }
+
+ pointer
+ operator-> () const
+ {
+ return *i_;
+ }
+
+ I const&
+ base () const
+ {
+ return i_;
+ }
+
+ public:
+ // Forward iterator requirements.
+ //
+ pointer_iterator&
+ operator++ ()
+ {
+ ++i_;
+ return *this;
+ }
+
+ pointer_iterator
+ operator++ (int)
+ {
+ pointer_iterator r (*this);
+ ++i_;
+ return r;
+ }
+
+ pointer_iterator&
+ operator-- ()
+ {
+ --i_;
+ return *this;
+ }
+
+ pointer_iterator
+ operator-- (int)
+ {
+ pointer_iterator r (*this);
+ --i_;
+ return r;
+ }
+
+ private:
+ I i_;
+ };
+
+ template <typename I>
+ inline bool
+ operator== (pointer_iterator<I> const& a, pointer_iterator<I> const& b)
+ {
+ return a.base () == b.base ();
+ }
+
+ template <typename I>
+ inline bool
+ operator!= (pointer_iterator<I> const& a, pointer_iterator<I> const& b)
+ {
+ return a.base () != b.base ();
+ }
+
+ template <typename I>
+ inline typename pointer_iterator<I>::difference_type
+ operator- (pointer_iterator<I> const& a, pointer_iterator<I> const& b)
+ {
+ return a.base () - b.base ();
+ }
+ }
+}
+
+#endif // LIBCUTL_CONTAINER_POINTER_ITERATOR_HXX
diff --git a/libcutl/exception.cxx b/libcutl/exception.cxx
new file mode 100644
index 0000000..bbe8ba5
--- /dev/null
+++ b/libcutl/exception.cxx
@@ -0,0 +1,15 @@
+// file : libcutl/exception.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <typeinfo>
+
+#include <libcutl/exception.hxx>
+
+namespace cutl
+{
+ char const* exception::
+ what () const noexcept
+ {
+ return typeid (*this).name ();
+ }
+}
diff --git a/libcutl/exception.hxx b/libcutl/exception.hxx
new file mode 100644
index 0000000..d9c069a
--- /dev/null
+++ b/libcutl/exception.hxx
@@ -0,0 +1,22 @@
+// file : libcutl/exception.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_EXCEPTION_HXX
+#define LIBCUTL_EXCEPTION_HXX
+
+#include <exception>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ struct LIBCUTL_EXPORT exception: std::exception
+ {
+ // By default return the exception type name ( typeid (*this).name () ).
+ //
+ virtual char const*
+ what () const noexcept;
+ };
+}
+
+#endif // LIBCUTL_EXCEPTION_HXX
diff --git a/libcutl/export.hxx b/libcutl/export.hxx
new file mode 100644
index 0000000..ccf1fbe
--- /dev/null
+++ b/libcutl/export.hxx
@@ -0,0 +1,40 @@
+// file : libcutl/export.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_EXPORT_HXX
+#define LIBCUTL_EXPORT_HXX
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBCUTL_STATIC) // Using static.
+# define LIBCUTL_EXPORT
+#elif defined(LIBCUTL_STATIC_BUILD) // Building static.
+# define LIBCUTL_EXPORT
+#elif defined(LIBCUTL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBCUTL_EXPORT __declspec(dllimport)
+# else
+# define LIBCUTL_EXPORT
+# endif
+#elif defined(LIBCUTL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBCUTL_EXPORT __declspec(dllexport)
+# else
+# define LIBCUTL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBCUTL_EXPORT // Using static or shared.
+#endif
+
+#endif // LIBCUTL_EXPORT_HXX
diff --git a/libcutl/fs/auto-remove.cxx b/libcutl/fs/auto-remove.cxx
new file mode 100644
index 0000000..9aad242
--- /dev/null
+++ b/libcutl/fs/auto-remove.cxx
@@ -0,0 +1,30 @@
+// file : libcutl/fs/auto-remove.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <cstdio> // std::remove
+#include <cerrno>
+
+#include <libcutl/fs/auto-remove.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ auto_remove::
+ ~auto_remove ()
+ {
+ if (!canceled_)
+ std::remove (path_.string ().c_str ()); // Ignore error.
+ }
+
+ auto_removes::
+ ~auto_removes ()
+ {
+ if (!canceled_)
+ {
+ for (paths::iterator i (paths_.begin ()); i != paths_.end (); ++i)
+ std::remove (i->string ().c_str ()); // Ignore error.
+ }
+ }
+ }
+}
diff --git a/libcutl/fs/auto-remove.hxx b/libcutl/fs/auto-remove.hxx
new file mode 100644
index 0000000..c55f6f9
--- /dev/null
+++ b/libcutl/fs/auto-remove.hxx
@@ -0,0 +1,82 @@
+// file : libcutl/fs/auto-remove.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_FS_AUTO_REMOVE_HXX
+#define LIBCUTL_FS_AUTO_REMOVE_HXX
+
+#include <vector>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/fs/exception.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ // Remove a file or an empty directory on destruction unless canceled.
+ //
+ struct LIBCUTL_EXPORT auto_remove
+ {
+ explicit
+ auto_remove (path const& p)
+ : path_ (p), canceled_ (false)
+ {
+ }
+
+ ~auto_remove ();
+
+ void
+ cancel ()
+ {
+ canceled_ = true;
+ }
+
+ private:
+ auto_remove (auto_remove const&);
+
+ auto_remove&
+ operator= (auto_remove const&);
+
+ private:
+ path path_;
+ bool canceled_;
+ };
+
+ // Remove a list of file or aempty directories on destruction unless
+ // canceled.
+ //
+ struct LIBCUTL_EXPORT auto_removes
+ {
+ auto_removes (): canceled_ (false) {}
+ ~auto_removes ();
+
+ void
+ add (path const& p)
+ {
+ paths_.push_back (p);
+ }
+
+ void
+ cancel ()
+ {
+ canceled_ = true;
+ }
+
+ private:
+ auto_removes (auto_removes const&);
+
+ auto_removes&
+ operator= (auto_removes const&);
+
+ private:
+ typedef std::vector<path> paths;
+
+ paths paths_;
+ bool canceled_;
+ };
+ }
+}
+
+#endif // LIBCUTL_FS_AUTO_REMOVE_HXX
diff --git a/libcutl/fs/exception.cxx b/libcutl/fs/exception.cxx
new file mode 100644
index 0000000..49fb9fe
--- /dev/null
+++ b/libcutl/fs/exception.cxx
@@ -0,0 +1,16 @@
+// file : libcutl/fs/exception.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/fs/exception.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ char const* error::
+ what () const noexcept
+ {
+ return "filesystem error";
+ }
+ }
+}
diff --git a/libcutl/fs/exception.hxx b/libcutl/fs/exception.hxx
new file mode 100644
index 0000000..511d86f
--- /dev/null
+++ b/libcutl/fs/exception.hxx
@@ -0,0 +1,36 @@
+// file : libcutl/fs/exception.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_FS_EXCEPTION_HXX
+#define LIBCUTL_FS_EXCEPTION_HXX
+
+#include <libcutl/exception.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ struct LIBCUTL_EXPORT error: exception
+ {
+ error (int code): code_ (code) {}
+
+ // Error code (errno).
+ //
+ int
+ code () const
+ {
+ return code_;
+ }
+
+ virtual char const*
+ what () const noexcept;
+
+ private:
+ int code_;
+ };
+ }
+}
+
+#endif // LIBCUTL_FS_EXCEPTION_HXX
diff --git a/libcutl/fs/path.cxx b/libcutl/fs/path.cxx
new file mode 100644
index 0000000..c163d8b
--- /dev/null
+++ b/libcutl/fs/path.cxx
@@ -0,0 +1,120 @@
+// file : libcutl/fs/path.cxx
+// license : MIT; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <stdlib.h> // _MAX_PATH
+# include <direct.h> // _[w]getcwd, _[w]chdir
+#else
+# include <stdlib.h> // mbstowcs, wcstombs
+# include <limits.h> // PATH_MAX
+# include <unistd.h> // getcwd, chdir
+#endif
+
+#ifndef _WIN32
+# ifndef PATH_MAX
+# define PATH_MAX 4096
+# endif
+#endif
+
+#include <libcutl/fs/path.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ char const* invalid_path_base::
+ what () const noexcept
+ {
+ return "invalid filesystem path";
+ }
+
+ //
+ // char
+ //
+
+ template <>
+ LIBCUTL_EXPORT basic_path<char> basic_path<char>::
+ current ()
+ {
+#ifdef _WIN32
+ char cwd[_MAX_PATH];
+ if(_getcwd(cwd, _MAX_PATH) == 0)
+ throw invalid_basic_path<char> (".");
+#else
+ char cwd[PATH_MAX];
+ if (getcwd (cwd, PATH_MAX) == 0)
+ throw invalid_basic_path<char> (".");
+#endif
+
+ return basic_path<char> (cwd);
+ }
+
+ template <>
+ LIBCUTL_EXPORT void basic_path<char>::
+ current (basic_path const& p)
+ {
+ string_type const& s (p.string ());
+
+ if (p.empty ())
+ throw invalid_basic_path<char> (s);
+
+#ifdef _WIN32
+ if(_chdir(s.c_str ()) != 0)
+ throw invalid_basic_path<char> (s);
+#else
+ if (chdir (s.c_str ()) != 0)
+ throw invalid_basic_path<char> (s);
+#endif
+ }
+
+ //
+ // wchar_t
+ //
+
+ template <>
+ LIBCUTL_EXPORT basic_path<wchar_t> basic_path<wchar_t>::
+ current ()
+ {
+#ifdef _WIN32
+ wchar_t wcwd[_MAX_PATH];
+ if(_wgetcwd(wcwd, _MAX_PATH) == 0)
+ throw invalid_basic_path<wchar_t> (L".");
+#else
+ char cwd[PATH_MAX];
+ if (getcwd (cwd, PATH_MAX) == 0)
+ throw invalid_basic_path<wchar_t> (L".");
+
+ wchar_t wcwd[PATH_MAX];
+ if (mbstowcs (wcwd, cwd, PATH_MAX) == size_type (-1))
+ throw invalid_basic_path<wchar_t> (L".");
+#endif
+
+ return basic_path<wchar_t> (wcwd);
+ }
+
+ template <>
+ LIBCUTL_EXPORT void basic_path<wchar_t>::
+ current (basic_path const& p)
+ {
+ string_type const& s (p.string ());
+
+ if (p.empty ())
+ throw invalid_basic_path<wchar_t> (s);
+
+#ifdef _WIN32
+ if(_wchdir(s.c_str ()) != 0)
+ throw invalid_basic_path<wchar_t> (s);
+#else
+ char ns[PATH_MAX + 1];
+
+ if (wcstombs (ns, s.c_str (), PATH_MAX) == size_type (-1))
+ throw invalid_basic_path<wchar_t> (s);
+
+ ns[PATH_MAX] = '\0';
+
+ if (chdir (ns) != 0)
+ throw invalid_basic_path<wchar_t> (s);
+#endif
+ }
+ }
+}
diff --git a/libcutl/fs/path.hxx b/libcutl/fs/path.hxx
new file mode 100644
index 0000000..1d2119c
--- /dev/null
+++ b/libcutl/fs/path.hxx
@@ -0,0 +1,332 @@
+// file : libcutl/fs/path.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_FS_PATH_HXX
+#define LIBCUTL_FS_PATH_HXX
+
+#include <string>
+#include <ostream>
+
+#include <libcutl/exception.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace fs
+ {
+ template <typename C>
+ class basic_path;
+
+ template <typename C>
+ struct path_traits
+ {
+ typedef std::basic_string<C> string_type;
+ typedef typename string_type::size_type size_type;
+
+ // Canonical directory and path seperators.
+ //
+#ifdef _WIN32
+ static C const directory_separator = '\\';
+ static C const path_separator = ';';
+#else
+ static C const directory_separator = '/';
+ static C const path_separator = ':';
+#endif
+
+ // Directory separator tests. On some platforms there
+ // could be multiple seperators. For example, on Windows
+ // we check for both '/' and '\'.
+ //
+ static bool
+ is_separator (C c)
+ {
+#ifdef _WIN32
+ return c == '\\' || c == '/';
+#else
+ return c == '/';
+#endif
+ }
+
+ static size_type
+ find_separator (string_type const& s, size_type pos = 0)
+ {
+ for (size_type n (s.size ()); pos < n; ++pos)
+ {
+ if (is_separator (s[pos]))
+ return pos;
+ }
+
+ return string_type::npos;
+ }
+
+ static size_type
+ rfind_separator (string_type const& s, size_type pos = string_type::npos)
+ {
+ if (pos == string_type::npos)
+ pos = s.size ();
+ else
+ pos++;
+
+ for (; pos > 0; --pos)
+ {
+ if (is_separator (s[pos - 1]))
+ return pos - 1;
+ }
+
+ return string_type::npos;
+ }
+
+ static int
+ compare (string_type const& l, string_type const& r)
+ {
+ size_type ln (l.size ()), rn (r.size ()), n (ln < rn ? ln : rn);
+ for (size_type i (0); i != n; ++i)
+ {
+#ifdef _WIN32
+ C lc (tolower (l[i])), rc (tolower (r[i]));
+#else
+ C lc (l[i]), rc (r[i]);
+#endif
+ if (is_separator (lc) && is_separator (rc))
+ continue;
+
+ if (lc < rc) return -1;
+ if (lc > rc) return 1;
+ }
+
+ return ln < rn ? -1 : (ln > rn ? 1 : 0);
+ }
+
+ private:
+#ifdef _WIN32
+ static C
+ tolower (C);
+#endif
+ };
+
+ template <typename C>
+ class invalid_basic_path;
+
+ typedef basic_path<char> path;
+ typedef invalid_basic_path<char> invalid_path;
+
+ typedef basic_path<wchar_t> wpath;
+ typedef invalid_basic_path<wchar_t> invalid_wpath;
+
+ //
+ //
+ class LIBCUTL_EXPORT invalid_path_base: exception
+ {
+ public:
+ virtual char const*
+ what () const noexcept;
+ };
+
+ template <typename C>
+ class invalid_basic_path: public invalid_path_base
+ {
+ public:
+ typedef std::basic_string<C> string_type;
+
+ invalid_basic_path (C const* p): path_ (p) {}
+ invalid_basic_path (string_type const& p): path_ (p) {}
+ ~invalid_basic_path () noexcept {}
+
+ string_type const&
+ path () const
+ {
+ return path_;
+ }
+
+ private:
+ string_type path_;
+ };
+
+ template <typename C>
+ class basic_path
+ {
+ public:
+ typedef std::basic_string<C> string_type;
+ typedef typename string_type::size_type size_type;
+
+ typedef path_traits<C> traits;
+
+ // Construct special empty path.
+ //
+ basic_path ()
+ {
+ }
+
+ explicit
+ basic_path (C const* s)
+ : path_ (s)
+ {
+ init ();
+ }
+
+ basic_path (C const* s, size_type n)
+ : path_ (s, n)
+ {
+ init ();
+ }
+
+ explicit
+ basic_path (string_type const& s)
+ : path_ (s)
+ {
+ init ();
+ }
+
+ void
+ swap (basic_path& p)
+ {
+ path_.swap (p.path_);
+ }
+
+ void
+ clear ()
+ {
+ path_.clear ();
+ }
+
+ static basic_path
+ current ();
+
+ static void
+ current (basic_path const&);
+
+ public:
+ bool
+ empty () const
+ {
+ return path_.empty ();
+ }
+
+ bool
+ absolute () const;
+
+ bool
+ relative () const
+ {
+ return !absolute ();
+ }
+
+ bool
+ root () const;
+
+ public:
+ // Return the path without the directory part.
+ //
+ basic_path
+ leaf () const;
+
+ // Return the directory part of the path or empty path if
+ // there is no directory.
+ //
+ basic_path
+ directory () const;
+
+ // Return the path without the extension, if any.
+ //
+ basic_path
+ base () const;
+
+ public:
+ // Normalize the path. This includes collapsing the '.' and '..'
+ // directories if possible, collapsing multiple directory
+ // separators, and converting all directory separators to the
+ // canonical form. Returns *this.
+ //
+ basic_path&
+ normalize ();
+
+ // Make the path absolute using the current directory unless
+ // it is already absolute.
+ //
+ basic_path&
+ complete ();
+
+ public:
+ basic_path
+ operator/ (basic_path const& x) const
+ {
+ basic_path r (*this);
+ r /= x;
+ return r;
+ }
+
+ basic_path&
+ operator/= (basic_path const&);
+
+ basic_path
+ operator+ (string_type const& s) const
+ {
+ return basic_path (path_ + s);
+ }
+
+ basic_path&
+ operator+= (string_type const& s)
+ {
+ path_ += s;
+ return *this;
+ }
+
+ // Note that comparison is case-insensitive if the filesystem is
+ // not case-sensitive (e.g., Windows).
+ //
+ bool
+ operator== (basic_path const& x) const
+ {
+ return traits::compare (path_, x.path_) == 0;
+ }
+
+ bool
+ operator!= (basic_path const& x) const
+ {
+ return !(*this == x);
+ }
+
+ bool
+ operator< (basic_path const& x) const
+ {
+ return traits::compare (path_, x.path_) < 0;
+ }
+
+ public:
+ const string_type&
+ string () const
+ {
+ return path_;
+ }
+
+ // If possible, return a POSIX representation of the path. For example,
+ // for a Windows path in the form foo\bar this function will return
+ // foo/bar. If it is not possible to create a POSIX representation for
+ // this path (e.g., c:\foo), this function will throw the invalid_path
+ // exception.
+ //
+ string_type
+ posix_string () const;
+
+ private:
+ void
+ init ();
+
+ private:
+ string_type path_;
+ };
+
+ template <typename C>
+ inline std::basic_ostream<C>&
+ operator<< (std::basic_ostream<C>& os, basic_path<C> const& p)
+ {
+ return os << p.string ();
+ }
+ }
+}
+
+#include <libcutl/fs/path.ixx>
+#include <libcutl/fs/path.txx>
+
+#endif // LIBCUTL_FS_PATH_HXX
diff --git a/libcutl/fs/path.ixx b/libcutl/fs/path.ixx
new file mode 100644
index 0000000..461a871
--- /dev/null
+++ b/libcutl/fs/path.ixx
@@ -0,0 +1,70 @@
+// file : libcutl/fs/path.ixx
+// license : MIT; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <cctype> // std::tolower
+# include <cwctype> // std::towlower
+#endif
+
+namespace cutl
+{
+ namespace fs
+ {
+#ifdef _WIN32
+ template <>
+ inline char path_traits<char>::
+ tolower (char c)
+ {
+ return std::tolower (c);
+ }
+
+ template <>
+ inline wchar_t path_traits<wchar_t>::
+ tolower (wchar_t c)
+ {
+ return std::towlower (c);
+ }
+#endif
+
+ template <typename C>
+ inline bool basic_path<C>::
+ absolute () const
+ {
+#ifdef _WIN32
+ return path_.size () > 1 && path_[1] == ':';
+#else
+ return !path_.empty () && traits::is_separator (path_[0]);
+#endif
+ }
+
+ template <typename C>
+ inline bool basic_path<C>::
+ root () const
+ {
+#ifdef _WIN32
+ return path_.size () == 2 && path_[1] == ':';
+#else
+ return path_.size () == 1 && traits::is_separator (path_[0]);
+#endif
+ }
+
+ template <typename C>
+ inline basic_path<C>& basic_path<C>::
+ complete ()
+ {
+ if (relative ())
+ *this = current () / *this;
+
+ return *this;
+ }
+
+#ifndef _WIN32
+ template <typename C>
+ inline typename basic_path<C>::string_type basic_path<C>::
+ posix_string () const
+ {
+ return string ();
+ }
+#endif
+ }
+}
diff --git a/libcutl/fs/path.txx b/libcutl/fs/path.txx
new file mode 100644
index 0000000..9993c99
--- /dev/null
+++ b/libcutl/fs/path.txx
@@ -0,0 +1,209 @@
+// file : libcutl/fs/path.txx
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+
+namespace cutl
+{
+ namespace fs
+ {
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ leaf () const
+ {
+ size_type p (traits::rfind_separator (path_));
+
+ return p != string_type::npos
+ ? basic_path (path_.c_str () + p + 1, path_.size () - p - 1)
+ : *this;
+ }
+
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ directory () const
+ {
+ if (root ())
+ return basic_path ();
+
+ size_type p (traits::rfind_separator (path_));
+
+ // Include the trailing slash so that we get correct behavior
+ // if directory is root.
+ //
+ return p != string_type::npos
+ ? basic_path (path_.c_str (), p + 1)
+ : basic_path ();
+ }
+
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ base () const
+ {
+ size_type i (path_.size ());
+
+ for (; i > 0; --i)
+ {
+ if (path_[i - 1] == '.')
+ break;
+
+ if (traits::is_separator (path_[i - 1]))
+ {
+ i = 0;
+ break;
+ }
+ }
+
+ // Weed out paths like ".txt" and "/.txt"
+ //
+ if (i > 1 && !traits::is_separator (path_[i - 2]))
+ {
+ return basic_path (path_.c_str (), i - 1);
+ }
+ else
+ return *this;
+ }
+
+#ifdef _WIN32
+ template <typename C>
+ typename basic_path<C>::string_type basic_path<C>::
+ posix_string () const
+ {
+ if (absolute ())
+ throw invalid_basic_path<C> (path_);
+
+ string_type r (path_);
+
+ // Translate Windows-style separators to the POSIX ones.
+ //
+ for (size_type i (0), n (r.size ()); i != n; ++i)
+ if (r[i] == '\\')
+ r[i] = '/';
+
+ return r;
+ }
+#endif
+
+ template <typename C>
+ basic_path<C>& basic_path<C>::
+ operator/= (basic_path<C> const& r)
+ {
+ if (r.absolute ())
+ throw invalid_basic_path<C> (r.path_);
+
+ if (path_.empty () || r.path_.empty ())
+ {
+ path_ += r.path_;
+ return *this;
+ }
+
+ if (!traits::is_separator (path_[path_.size () - 1]))
+ path_ += traits::directory_separator;
+
+ path_ += r.path_;
+
+ return *this;
+ }
+
+ template <typename C>
+ basic_path<C>& basic_path<C>::
+ normalize ()
+ {
+ if (empty ())
+ return *this;
+
+ bool abs (absolute ());
+
+ typedef std::vector<string_type> paths;
+ paths ps;
+
+ for (size_type b (0), e (traits::find_separator (path_)),
+ n (path_.size ());;
+ e = traits::find_separator (path_, b))
+ {
+ string_type s (path_, b, e == string_type::npos ? e : e - b);
+ ps.push_back (s);
+
+ if (e == string_type::npos)
+ break;
+
+ ++e;
+
+ while (e < n && traits::is_separator (path_[e]))
+ ++e;
+
+ if (e == n)
+ break;
+
+ b = e;
+ }
+
+ // First collapse '.' and '..'.
+ //
+ paths r;
+
+ for (typename paths::const_iterator i (ps.begin ()), e (ps.end ());
+ i != e; ++i)
+ {
+ string_type const& s (*i);
+ size_type n (s.size ());
+
+ if (n == 1 && s[0] == '.')
+ continue;
+
+ if (n == 2 && s[0] == '.' && s[1] == '.')
+ {
+ // Pop the last directory from r unless it is '..'.
+ //
+ if (!r.empty ())
+ {
+ string_type const& s1 (r.back ());
+
+ if (!(s1.size () == 2 && s1[0] == '.' && s1[1] == '.'))
+ {
+ // Cannot go past the root directory.
+ //
+ if (abs && r.size () == 1)
+ throw invalid_basic_path<C> (path_);
+
+ r.pop_back ();
+ continue;
+ }
+ }
+ }
+
+ r.push_back (s);
+ }
+
+ // Reassemble the path.
+ //
+ string_type p;
+
+ for (typename paths::const_iterator i (r.begin ()), e (r.end ());
+ i != e;)
+ {
+ p += *i;
+
+ if (++i != e)
+ p += traits::directory_separator;
+ }
+
+ if (p.empty () && !r.empty ())
+ p += traits::directory_separator; // Root directory.
+
+ path_.swap (p);
+ return *this;
+ }
+
+ template <typename C>
+ void basic_path<C>::
+ init ()
+ {
+ // Strip trailing slashes except for the case where the single
+ // slash represents the root directory.
+ //
+ size_type n (path_.size ());
+ for (; n > 1 && traits::is_separator (path_[n - 1]); --n) ;
+ path_.resize (n);
+ }
+ }
+}
diff --git a/libcutl/meta/answer.hxx b/libcutl/meta/answer.hxx
new file mode 100644
index 0000000..4a94c90
--- /dev/null
+++ b/libcutl/meta/answer.hxx
@@ -0,0 +1,23 @@
+// file : libcutl/meta/answer.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_ANSWER_HXX
+#define LIBCUTL_META_ANSWER_HXX
+
+namespace cutl
+{
+ namespace meta
+ {
+ struct yes
+ {
+ char filling;
+ };
+
+ struct no
+ {
+ char filling[2];
+ };
+ }
+}
+
+#endif // LIBCUTL_META_ANSWER_HXX
diff --git a/libcutl/meta/class-p.hxx b/libcutl/meta/class-p.hxx
new file mode 100644
index 0000000..fc7a111
--- /dev/null
+++ b/libcutl/meta/class-p.hxx
@@ -0,0 +1,26 @@
+// file : libcutl/meta/class-p.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_CLASS_HXX
+#define LIBCUTL_META_CLASS_HXX
+
+#include <libcutl/meta/answer.hxx>
+
+namespace cutl
+{
+ 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 // LIBCUTL_META_CLASS_HXX
diff --git a/libcutl/meta/polymorphic-p.hxx b/libcutl/meta/polymorphic-p.hxx
new file mode 100644
index 0000000..13fb531
--- /dev/null
+++ b/libcutl/meta/polymorphic-p.hxx
@@ -0,0 +1,49 @@
+// file : libcutl/meta/polymorphic-p.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_POLYMORPHIC_HXX
+#define LIBCUTL_META_POLYMORPHIC_HXX
+
+#include <libcutl/meta/class-p.hxx>
+#include <libcutl/meta/remove-cv.hxx>
+
+namespace cutl
+{
+ 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 () noexcept;
+ };
+
+ static const bool r = sizeof (t1) == sizeof (t2);
+ };
+
+ static const bool r = impl<X, class_p<X>::r>::r;
+ };
+ }
+}
+
+#endif // LIBCUTL_META_POLYMORPHIC_HXX
diff --git a/libcutl/meta/remove-c.hxx b/libcutl/meta/remove-c.hxx
new file mode 100644
index 0000000..7f5af3e
--- /dev/null
+++ b/libcutl/meta/remove-c.hxx
@@ -0,0 +1,25 @@
+// file : libcutl/meta/remove-c.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_REMOVE_C_HXX
+#define LIBCUTL_META_REMOVE_C_HXX
+
+namespace cutl
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_c
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_c<X const>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // LIBCUTL_META_REMOVE_C_HXX
diff --git a/libcutl/meta/remove-cv.hxx b/libcutl/meta/remove-cv.hxx
new file mode 100644
index 0000000..54780a6
--- /dev/null
+++ b/libcutl/meta/remove-cv.hxx
@@ -0,0 +1,22 @@
+// file : libcutl/meta/remove-cv.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_REMOVE_CV_HXX
+#define LIBCUTL_META_REMOVE_CV_HXX
+
+#include <libcutl/meta/remove-c.hxx>
+#include <libcutl/meta/remove-v.hxx>
+
+namespace cutl
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_cv
+ {
+ typedef typename remove_v<typename remove_c<X>::r>::r r;
+ };
+ }
+}
+
+#endif // LIBCUTL_META_REMOVE_CV_HXX
diff --git a/libcutl/meta/remove-p.hxx b/libcutl/meta/remove-p.hxx
new file mode 100644
index 0000000..dc6a946
--- /dev/null
+++ b/libcutl/meta/remove-p.hxx
@@ -0,0 +1,25 @@
+// file : libcutl/meta/remove-p.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_REMOVE_P_HXX
+#define LIBCUTL_META_REMOVE_P_HXX
+
+namespace cutl
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_p
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_p<X*>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // LIBCUTL_META_REMOVE_P_HXX
diff --git a/libcutl/meta/remove-v.hxx b/libcutl/meta/remove-v.hxx
new file mode 100644
index 0000000..97439c3
--- /dev/null
+++ b/libcutl/meta/remove-v.hxx
@@ -0,0 +1,25 @@
+// file : libcutl/meta/remove-v.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_META_REMOVE_V_HXX
+#define LIBCUTL_META_REMOVE_V_HXX
+
+namespace cutl
+{
+ namespace meta
+ {
+ template <typename X>
+ struct remove_v
+ {
+ typedef X r;
+ };
+
+ template <typename X>
+ struct remove_v<X volatile>
+ {
+ typedef X r;
+ };
+ }
+}
+
+#endif // LIBCUTL_META_REMOVE_V_HXX
diff --git a/libcutl/re.hxx b/libcutl/re.hxx
new file mode 100644
index 0000000..15b153c
--- /dev/null
+++ b/libcutl/re.hxx
@@ -0,0 +1,280 @@
+// file : libcutl/re.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_RE_HXX
+#define LIBCUTL_RE_HXX
+
+#include <string>
+#include <ostream>
+
+#include <libcutl/exception.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ namespace re
+ {
+ struct LIBCUTL_EXPORT format_base: exception
+ {
+ format_base (std::string const& d): description_ (d) {}
+ ~format_base () noexcept {}
+
+ std::string const&
+ description () const
+ {
+ return description_;
+ }
+
+ virtual char const*
+ what () const noexcept;
+
+ protected:
+ std::string description_;
+ };
+
+ template <typename C>
+ struct basic_format: format_base
+ {
+ basic_format (std::basic_string<C> const& e, std::string const& d)
+ : format_base (d), regex_ (e) {}
+ ~basic_format () noexcept {}
+
+ std::basic_string<C> const&
+ regex () const
+ {
+ return regex_;
+ }
+
+ private:
+ std::basic_string<C> regex_;
+ };
+
+ typedef basic_format<char> format;
+ typedef basic_format<wchar_t> wformat;
+
+ // Regular expression pattern.
+ //
+ template <typename C>
+ struct basic_regex
+ {
+ typedef std::basic_string<C> string_type;
+
+ ~basic_regex ();
+
+ basic_regex (): impl_ (0) {init (0, false);}
+
+ explicit
+ basic_regex (string_type const& s, bool icase = false)
+ : impl_ (0)
+ {
+ init (&s, icase);
+ }
+
+ basic_regex&
+ operator= (string_type const& s)
+ {
+ init (&s, false);
+ return *this;
+ }
+
+ basic_regex&
+ assign (string_type const& s, bool icase = false)
+ {
+ init (&s, icase);
+ return *this;
+ }
+
+ basic_regex (basic_regex const&);
+
+ basic_regex&
+ operator= (basic_regex const&);
+
+ public:
+ bool
+ match (string_type const&) const;
+
+ bool
+ search (string_type const&) const;
+
+ string_type
+ replace (string_type const& s,
+ string_type const& sub,
+ bool first_only = false) const;
+
+ public:
+ string_type const&
+ str () const
+ {
+ return str_;
+ }
+
+ bool
+ empty () const
+ {
+ return str_.empty ();
+ }
+
+ private:
+ void
+ init (string_type const*, bool);
+
+ private:
+ struct impl;
+
+ string_type str_; // Text representation of regex.
+ impl* impl_;
+ };
+
+ template <typename C>
+ inline std::basic_ostream<C>&
+ operator<< (std::basic_ostream<C>& os, basic_regex<C> const& r)
+ {
+ return os << r.str ();
+ }
+
+ typedef basic_regex<char> regex;
+ typedef basic_regex<wchar_t> wregex;
+
+ // Regular expression pattern and substituation.
+ //
+ template <typename C>
+ struct basic_regexsub
+ {
+ typedef basic_regex<C> regex_type;
+ typedef std::basic_string<C> string_type;
+
+ basic_regexsub () {}
+
+ // Expression is of the form /regex/substitution/ where '/' can
+ // be replaced with any delimiter. Delimiters must be escaped in
+ // regex and substitution using back slashes (e.g., "\/"). Back
+ // slashes themselves can be escaped using the double back slash
+ // sequence (e.g., "\\").
+ //
+ explicit
+ basic_regexsub (string_type const& e) {init (e);}
+
+ basic_regexsub (string_type const& regex, string_type const& sub)
+ : regex_ (regex), sub_ (sub)
+ {
+ }
+
+ basic_regexsub (regex_type const& regex, string_type const& sub)
+ : regex_ (regex), sub_ (sub)
+ {
+ }
+
+ basic_regexsub&
+ operator= (string_type const& e)
+ {
+ init (e);
+ return *this;
+ }
+
+ public:
+ bool
+ match (string_type const& s) const
+ {
+ return regex_.match (s);
+ }
+
+ bool
+ search (string_type const& s) const
+ {
+ return regex_.search (s);
+ }
+
+ string_type
+ replace (string_type const& s, bool first_only = false) const
+ {
+ return regex_.replace (s, sub_, first_only);
+ }
+
+ public:
+ const regex_type&
+ regex () const
+ {
+ return regex_;
+ }
+
+ const string_type&
+ substitution () const
+ {
+ return sub_;
+ }
+
+ bool
+ empty () const
+ {
+ return sub_.empty () && regex_.empty ();
+ }
+
+ private:
+ void
+ init (string_type const&);
+
+ private:
+ regex_type regex_;
+ string_type sub_;
+ };
+
+ typedef basic_regexsub<char> regexsub;
+ typedef basic_regexsub<wchar_t> wregexsub;
+
+ // Once-off regex execution.
+ //
+ template <typename C>
+ inline bool
+ match (std::basic_string<C> const& s, std::basic_string<C> const& regex)
+ {
+ basic_regex<C> r (regex);
+ return r.match (s);
+ }
+
+ template <typename C>
+ inline bool
+ search (std::basic_string<C> const& s, std::basic_string<C> const& regex)
+ {
+ basic_regex<C> r (regex);
+ return r.search (s);
+ }
+
+ template <typename C>
+ inline std::basic_string<C>
+ replace (std::basic_string<C> const& s,
+ std::basic_string<C> const& regex,
+ std::basic_string<C> const& sub,
+ bool first_only = false)
+ {
+ basic_regex<C> r (regex);
+ return r.replace (s, sub, first_only);
+ }
+
+ template <typename C>
+ inline std::basic_string<C>
+ replace (std::basic_string<C> const& s,
+ std::basic_string<C> const& regexsub, // /regex/subst/
+ bool first_only = false)
+ {
+ basic_regexsub<C> r (regexsub);
+ return r.replace (s, first_only);
+ }
+
+ // Utility function for parsing expressions in the form /regex/subst/
+ // where '/' can be replaced with any delimiter. This function handles
+ // escaping. It return the position of the next delimiter and stores
+ // the unescaped chunk in result or throws the format exception if
+ // the expression is invalid.
+ //
+ template <typename C>
+ typename std::basic_string<C>::size_type
+ parse (std::basic_string<C> const& s,
+ typename std::basic_string<C>::size_type start,
+ std::basic_string<C>& result);
+ }
+}
+
+#include <libcutl/re/re.txx>
+
+#endif // LIBCUTL_RE_HXX
diff --git a/libcutl/re/re.cxx b/libcutl/re/re.cxx
new file mode 100644
index 0000000..524c416
--- /dev/null
+++ b/libcutl/re/re.cxx
@@ -0,0 +1,462 @@
+// file : libcutl/re/re.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/re.hxx>
+
+// It is either C++11 regex or Boost.
+//
+// Note that some compiler/runtime combinations don't have usable C++11
+// regex. For example Clang 3.5 with libstdc++ from GCC 4.9. In this case you
+// can fall back to using Boost regex by passing -DLIBCUTL_BOOST_REGEX
+// preprocessor option when building libcutl.
+//
+// @@ Should this rather be a (custom) config.* variable?
+//
+#if !defined(LIBCUTL_BOOST_REGEX)
+# include <regex>
+# include <locale>
+# include <cstddef> // size_t
+#else
+# ifndef LIBCUTL_BOOST_REGEX
+# define LIBCUTL_BOOST_REGEX
+# endif
+# include <boost/tr1/regex.hpp>
+#endif
+
+using namespace std;
+
+namespace cutl
+{
+ namespace re
+ {
+#ifdef LIBCUTL_BOOST_REGEX
+ namespace ire = std::tr1;
+#else
+ namespace ire = std;
+#endif
+
+ //
+ // format_base
+ //
+
+ char const* format_base::
+ what () const noexcept
+ {
+ return description_.c_str ();
+ }
+
+ //
+ // basic_regex
+ //
+ template <typename C>
+ struct basic_regex<C>::impl
+ {
+ typedef basic_string<C> string_type;
+ typedef ire::basic_regex<C> regex_type;
+ typedef typename regex_type::flag_type flag_type;
+
+ impl () {}
+ impl (regex_type const& r): r (r) {}
+ impl (string_type const& s, bool icase)
+ {
+ flag_type f (ire::regex_constants::ECMAScript);
+
+ if (icase)
+ f |= ire::regex_constants::icase;
+
+ r.assign (s, f);
+ }
+
+ regex_type r;
+ };
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<char>::
+ ~basic_regex ()
+ {
+ delete impl_;
+ }
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<wchar_t>::
+ ~basic_regex ()
+ {
+ delete impl_;
+ }
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<char>::
+ basic_regex (basic_regex const& r)
+ : str_ (r.str_), impl_ (new impl (r.impl_->r))
+ {
+ }
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<wchar_t>::
+ basic_regex (basic_regex const& r)
+ : str_ (r.str_), impl_ (new impl (r.impl_->r))
+ {
+ }
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<char>& basic_regex<char>::
+ operator= (basic_regex const& r)
+ {
+ string_type tmp (r.str_);
+ impl_->r = r.impl_->r;
+ str_.swap (tmp);
+ return *this;
+ }
+
+ template <>
+ LIBCUTL_EXPORT basic_regex<wchar_t>& basic_regex<wchar_t>::
+ operator= (basic_regex const& r)
+ {
+ string_type tmp (r.str_);
+ impl_->r = r.impl_->r;
+ str_.swap (tmp);
+ return *this;
+ }
+
+ template <>
+ LIBCUTL_EXPORT void basic_regex<char>::
+ init (string_type const* s, bool icase)
+ {
+ string_type tmp (s == 0 ? string_type () : *s);
+
+ try
+ {
+ if (impl_ == 0)
+ impl_ = s == 0 ? new impl : new impl (*s, icase);
+ else
+ {
+ impl::flag_type f (ire::regex_constants::ECMAScript);
+
+ if (icase)
+ f |= ire::regex_constants::icase;
+
+ impl_->r.assign (*s, f);
+ }
+ }
+ catch (ire::regex_error const& e)
+ {
+ throw basic_format<char> (s == 0 ? "" : *s, e.what ());
+ }
+
+ str_.swap (tmp);
+ }
+
+ template <>
+ LIBCUTL_EXPORT void basic_regex<wchar_t>::
+ init (string_type const* s, bool icase)
+ {
+ string_type tmp (s == 0 ? string_type () : *s);
+
+ try
+ {
+ if (impl_ == 0)
+ impl_ = s == 0 ? new impl : new impl (*s, icase);
+ else
+ {
+ impl::flag_type f (ire::regex_constants::ECMAScript);
+
+ if (icase)
+ f |= ire::regex_constants::icase;
+
+ impl_->r.assign (*s, f);
+ }
+ }
+ catch (ire::regex_error const& e)
+ {
+ throw basic_format<wchar_t> (s == 0 ? L"" : *s, e.what ());
+ }
+
+ str_.swap (tmp);
+ }
+
+ template <>
+ LIBCUTL_EXPORT bool basic_regex<char>::
+ match (string_type const& s) const
+ {
+ return ire::regex_match (s, impl_->r);
+ }
+
+ template <>
+ LIBCUTL_EXPORT bool basic_regex<wchar_t>::
+ match (string_type const& s) const
+ {
+ return ire::regex_match (s, impl_->r);
+ }
+
+ template <>
+ LIBCUTL_EXPORT bool basic_regex<char>::
+ search (string_type const& s) const
+ {
+ return ire::regex_search (s, impl_->r);
+ }
+
+ template <>
+ LIBCUTL_EXPORT bool basic_regex<wchar_t>::
+ search (string_type const& s) const
+ {
+ return ire::regex_search (s, impl_->r);
+ }
+
+ // If we are using C++11 regex then extend the standard ECMA-262
+ // substitution escape sequences with a subset of Perl sequences:
+ //
+ // \\, \u, \l, \U, \L, \E, \1, ..., \9
+ //
+ // Notes and limitations:
+ //
+ // - The only valid regex_constants flags are match_default,
+ // format_first_only (format_no_copy can easily be supported).
+ //
+ // - If backslash doesn't start any of the listed sequences then it is
+ // silently dropped and the following character is copied as is.
+ //
+ // - The character case conversion is performed according to the global
+ // C++ locale (which is, unless changed, is the same as C locale and
+ // both default to the POSIX locale aka "C").
+ //
+ template <typename C>
+ static basic_string<C>
+ regex_replace_ex (const basic_string<C>& s,
+ const ire::basic_regex<C>& re,
+ const basic_string<C>& fmt,
+ ire::regex_constants::match_flag_type flags)
+ {
+#ifdef LIBCUTL_BOOST_REGEX
+ // Boost regex already does what we need.
+ //
+ return ire::regex_replace (s, re, fmt, flags);
+#else
+ using string_type = basic_string<C>;
+ using str_it = typename string_type::const_iterator;
+ using regex_it = regex_iterator<str_it>;
+
+ bool first_only ((flags & regex_constants::format_first_only) ==
+ regex_constants::format_first_only);
+
+ locale cl; // Copy of the global C++ locale.
+ string_type r;
+
+ // Beginning of the last unmatched substring.
+ //
+ str_it ub (s.begin ());
+
+ for (regex_it b (s.begin (), s.end (), re, flags), i (b), e; i != e; ++i)
+ {
+ const match_results<str_it>& m (*i);
+
+ // Copy the preceeding unmatched substring, save the beginning of the
+ // one that follows.
+ //
+ r.append (ub, m.prefix ().second);
+ ub = m.suffix ().first;
+
+ if (first_only && i != b)
+ r.append (m[0].first, m[0].second); // Append matched substring.
+ else
+ {
+ // The standard implementation calls m.format() here. We perform our
+ // own formatting.
+ //
+ // Note that we are using char type literals with the assumption
+ // that being ASCII characters they will be properly "widened" to
+ // the corresponding literals of the C template parameter type.
+ //
+ auto digit = [] (C c) -> int
+ {
+ return c >= '0' && c <= '9' ? c - '0' : -1;
+ };
+
+ enum class case_conv {none, upper, lower, upper_once, lower_once}
+ mode (case_conv::none);
+
+ auto conv_chr = [&mode, &cl] (C c) -> C
+ {
+ switch (mode)
+ {
+ case case_conv::upper_once: mode = case_conv::none; // Fall through.
+ case case_conv::upper: c = toupper (c, cl); break;
+ case case_conv::lower_once: mode = case_conv::none; // Fall through.
+ case case_conv::lower: c = tolower (c, cl); break;
+ case case_conv::none: break;
+ }
+ return c;
+ };
+
+ auto append_chr = [&r, &conv_chr] (C c)
+ {
+ r.push_back (conv_chr (c));
+ };
+
+ auto append_str = [&r, &mode, &conv_chr] (str_it b, str_it e)
+ {
+ // Optimize for the common case.
+ //
+ if (mode == case_conv::none)
+ r.append (b, e);
+ else
+ {
+ for (str_it i (b); i != e; ++i)
+ r.push_back (conv_chr (*i));
+ }
+ };
+
+ size_t n (fmt.size ());
+ for (size_t i (0); i < n; ++i)
+ {
+ C c (fmt[i]);
+
+ switch (c)
+ {
+ case '$':
+ {
+ // Check if this is a $-based escape sequence. Interpret it
+ // accordingly if that's the case, treat '$' as a regular
+ // character otherwise.
+ //
+ c = fmt[++i]; // '\0' if last.
+
+ switch (c)
+ {
+ case '$': append_chr (c); break;
+ case '&': append_str (m[0].first, m[0].second); break;
+ case '`':
+ {
+ append_str (m.prefix ().first, m.prefix ().second);
+ break;
+ }
+ case '\'':
+ {
+ append_str (m.suffix ().first, m.suffix ().second);
+ break;
+ }
+ default:
+ {
+ // Check if this is a sub-expression 1-based index ($n or
+ // $nn). Append the matching substring if that's the case.
+ // Treat '$' as a regular character otherwise. Index
+ // greater than the sub-expression count is silently
+ // ignored.
+ //
+ int si (digit (c));
+ if (si >= 0)
+ {
+ int d;
+ if ((d = digit (fmt[i + 1])) >= 0) // '\0' if last.
+ {
+ si = si * 10 + d;
+ ++i;
+ }
+ }
+
+ if (si > 0)
+ {
+ // m[0] refers to the matched substring.
+ //
+ if (static_cast<size_t> (si) < m.size ())
+ append_str (m[si].first, m[si].second);
+ }
+ else
+ {
+ // Not a $-based escape sequence so treat '$' as a
+ // regular character.
+ //
+ --i;
+ append_chr ('$');
+ }
+
+ break;
+ }
+ }
+
+ break;
+ }
+ case '\\':
+ {
+ c = fmt[++i]; // '\0' if last.
+
+ switch (c)
+ {
+ case '\\': append_chr (c); break;
+
+ case 'u': mode = case_conv::upper_once; break;
+ case 'l': mode = case_conv::lower_once; break;
+ case 'U': mode = case_conv::upper; break;
+ case 'L': mode = case_conv::lower; break;
+ case 'E': mode = case_conv::none; break;
+ default:
+ {
+ // Check if this is a sub-expression 1-based index. Append
+ // the matching substring if that's the case, Skip '\\'
+ // otherwise. Index greater than the sub-expression count
+ // is silently ignored.
+ //
+ int si (digit (c));
+ if (si > 0)
+ {
+ // m[0] refers to the matched substring.
+ //
+ if (static_cast<size_t> (si) < m.size ())
+ append_str (m[si].first, m[si].second);
+ }
+ else
+ --i;
+
+ break;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ // Append a regular character.
+ //
+ append_chr (c);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ r.append (ub, s.end ()); // Append the rightmost non-matched substring.
+ return r;
+#endif
+ }
+
+ template <>
+ LIBCUTL_EXPORT string basic_regex<char>::
+ replace (string_type const& s,
+ string_type const& sub,
+ bool first_only) const
+ {
+ ire::regex_constants::match_flag_type f (
+ ire::regex_constants::format_default);
+
+ if (first_only)
+ f |= ire::regex_constants::format_first_only;
+
+ return regex_replace_ex (s, impl_->r, sub, f);
+ }
+
+ template <>
+ LIBCUTL_EXPORT wstring basic_regex<wchar_t>::
+ replace (string_type const& s,
+ string_type const& sub,
+ bool first_only) const
+ {
+ ire::regex_constants::match_flag_type f (
+ ire::regex_constants::format_default);
+
+ if (first_only)
+ f |= ire::regex_constants::format_first_only;
+
+ return regex_replace_ex (s, impl_->r, sub, f);
+ }
+ }
+}
diff --git a/libcutl/re/re.txx b/libcutl/re/re.txx
new file mode 100644
index 0000000..d125ad8
--- /dev/null
+++ b/libcutl/re/re.txx
@@ -0,0 +1,68 @@
+// file : libcutl/re/re.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace re
+ {
+ //
+ // basic_regexsub
+ //
+ template <typename C>
+ void basic_regexsub<C>::
+ init (string_type const& s)
+ {
+ string_type r;
+ typename string_type::size_type p (parse (s, 0, r));
+ regex_ = r;
+ p = parse (s, p, sub_);
+ if (p + 1 < s.size ())
+ throw basic_format<C> (s, "junk after third delimiter");
+ }
+
+ //
+ // parse()
+ //
+ template <typename C>
+ typename std::basic_string<C>::size_type
+ parse (std::basic_string<C> const& s,
+ typename std::basic_string<C>::size_type p,
+ std::basic_string<C>& r)
+ {
+ r.clear ();
+ typename std::basic_string<C>::size_type n (s.size ());
+
+ if (p >= n)
+ throw basic_format<C> (s, "empty expression");
+
+ C d (s[p++]);
+
+ for (; p < n; ++p)
+ {
+ if (s[p] == d)
+ break;
+
+ if (s[p] == '\\')
+ {
+ if (++p < n)
+ {
+ // Pass the escape sequence through unless it is the delimiter.
+ //
+ if (s[p] != d)
+ r += '\\';
+
+ r += s[p];
+ }
+ // else {We ran out of stuff before finding the delimiter.}
+ }
+ else
+ r += s[p];
+ }
+
+ if (p == n)
+ throw basic_format<C> (s, "missing closing delimiter");
+
+ return p;
+ }
+ }
+}
diff --git a/libcutl/shared-ptr.hxx b/libcutl/shared-ptr.hxx
new file mode 100644
index 0000000..d9441c3
--- /dev/null
+++ b/libcutl/shared-ptr.hxx
@@ -0,0 +1,157 @@
+// file : libcutl/shared-ptr.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_SHARED_PTR_HXX
+#define LIBCUTL_SHARED_PTR_HXX
+
+#include <libcutl/shared-ptr/base.hxx>
+
+namespace cutl
+{
+ 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 = 0)
+ {
+ 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_;
+ };
+
+ template <typename X, typename Y>
+ inline bool
+ operator== (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () == y.get ();
+ }
+
+ template <typename X, typename Y>
+ inline bool
+ operator!= (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () != y.get ();
+ }
+}
+
+#endif // LIBCUTL_SHARED_PTR_HXX
diff --git a/libcutl/shared-ptr/base.cxx b/libcutl/shared-ptr/base.cxx
new file mode 100644
index 0000000..a3bc774
--- /dev/null
+++ b/libcutl/shared-ptr/base.cxx
@@ -0,0 +1,61 @@
+// file : libcutl/shared-ptr/base.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/shared-ptr/base.hxx>
+
+using std::size_t;
+
+//
+//
+cutl::share shared = cutl::share (1);
+cutl::share exclusive = cutl::share (2);
+
+//
+//
+namespace cutl
+{
+ char const* not_shared::
+ what () const noexcept
+ {
+ return "object is not shared";
+ }
+}
+
+//
+//
+void*
+operator new (size_t n, cutl::share s)
+{
+ 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, cutl::share s) noexcept
+{
+ // 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/libcutl/shared-ptr/base.hxx b/libcutl/shared-ptr/base.hxx
new file mode 100644
index 0000000..f49a532
--- /dev/null
+++ b/libcutl/shared-ptr/base.hxx
@@ -0,0 +1,91 @@
+// file : libcutl/shared-ptr/base.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_SHARED_PTR_BASE_HXX
+#define LIBCUTL_SHARED_PTR_BASE_HXX
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <libcutl/exception.hxx>
+
+#include <libcutl/export.hxx>
+
+namespace cutl
+{
+ struct share
+ {
+ explicit
+ share (char id);
+
+ bool
+ operator== (share) const;
+
+ private:
+ char id_;
+ };
+}
+
+extern LIBCUTL_EXPORT cutl::share shared;
+extern LIBCUTL_EXPORT cutl::share exclusive;
+
+LIBCUTL_EXPORT void*
+operator new (std::size_t, cutl::share);
+
+LIBCUTL_EXPORT void
+operator delete (void*, cutl::share) noexcept;
+
+namespace cutl
+{
+ struct LIBCUTL_EXPORT not_shared: exception
+ {
+ virtual char const*
+ what () const noexcept;
+ };
+
+ struct LIBCUTL_EXPORT 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);
+
+ void
+ operator delete (void*, share) noexcept;
+
+ void
+ operator delete (void*) noexcept;
+
+ 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 <libcutl/shared-ptr/base.ixx>
+#include <libcutl/shared-ptr/base.txx>
+
+#endif // LIBCUTL_SHARED_PTR_BASE_HXX
diff --git a/libcutl/shared-ptr/base.ixx b/libcutl/shared-ptr/base.ixx
new file mode 100644
index 0000000..d4fe1ea
--- /dev/null
+++ b/libcutl/shared-ptr/base.ixx
@@ -0,0 +1,77 @@
+// file : libcutl/shared-ptr/base.ixx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ // 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)
+ {
+ return ::operator new (n);
+ }
+
+ inline void shared_base::
+ operator delete (void* p, share) noexcept
+ {
+ ::operator delete (p);
+ }
+
+ inline void shared_base::
+ operator delete (void* p) noexcept
+ {
+ ::operator delete (p);
+ }
+}
diff --git a/libcutl/shared-ptr/base.txx b/libcutl/shared-ptr/base.txx
new file mode 100644
index 0000000..6bd3ed8
--- /dev/null
+++ b/libcutl/shared-ptr/base.txx
@@ -0,0 +1,198 @@
+// file : libcutl/shared-ptr/base.txx
+// license : MIT; see accompanying LICENSE file
+
+#include <libcutl/meta/answer.hxx>
+#include <libcutl/meta/polymorphic-p.hxx>
+
+namespace cutl
+{
+ 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 Z>
+ counter_ops (counter_ops<Z, Z> const& x) : counter_ (x.counter_) {}
+
+ counter_ops&
+ operator= (counter_ops const& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (counter_ops<Z, Z> 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*) {}
+ counter_ops (counter_ops const&) {}
+
+ template <typename Z>
+ counter_ops (counter_ops<shared_base, Z> const&) {}
+
+ counter_ops&
+ operator= (counter_ops const&)
+ {
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (counter_ops<shared_base, Z> const&)
+ {
+ return *this;
+ }
+
+ 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/libcutl/static-ptr.hxx b/libcutl/static-ptr.hxx
new file mode 100644
index 0000000..d5750d4
--- /dev/null
+++ b/libcutl/static-ptr.hxx
@@ -0,0 +1,73 @@
+// file : libcutl/static-ptr.hxx
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_STATIC_PTR_HXX
+#define LIBCUTL_STATIC_PTR_HXX
+
+#include <cstddef> // std::size_t
+
+namespace cutl
+{
+ // This class template implements Jerry Schwarz's static
+ // initialization technique commonly found in iostream
+ // implementations.
+ //
+ // The second template argument is used to make sure the
+ // instantiation of static_ptr is unique.
+ //
+ template <typename X, typename ID>
+ class static_ptr
+ {
+ public:
+ static_ptr ()
+ {
+ if (count_ == 0)
+ x_ = new X;
+
+ ++count_;
+ }
+
+ ~static_ptr ()
+ {
+ if (--count_ == 0)
+ delete x_;
+ }
+
+ private:
+ static_ptr (static_ptr const&);
+
+ static_ptr&
+ operator= (static_ptr const&);
+
+ public:
+ X*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ X&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ X*
+ get () const
+ {
+ return x_;
+ }
+
+ private:
+ static X* x_;
+ static std::size_t count_;
+ };
+
+ template <typename X, typename ID>
+ X* static_ptr<X, ID>::x_ = 0;
+
+ template <typename X, typename ID>
+ std::size_t static_ptr<X, ID>::count_ = 0;
+}
+
+#endif // LIBCUTL_STATIC_PTR_HXX
diff --git a/libcutl/version.hxx b/libcutl/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libcutl/version.hxx
diff --git a/libcutl/version.hxx.in b/libcutl/version.hxx.in
new file mode 100644
index 0000000..1edede7
--- /dev/null
+++ b/libcutl/version.hxx.in
@@ -0,0 +1,38 @@
+// file : libcutl/version.hxx.in -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBCUTL_VERSION // Note: using the version macro itself.
+
+// The numeric version format is AAAAABBBBBCCCCCDDDE where:
+//
+// AAAAA - major version number
+// BBBBB - minor version number
+// CCCCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
+//
+// Version AAAAABBBBBCCCCCDDDE
+//
+// 0.1.0 0000000001000000000
+// 0.1.2 0000000001000020000
+// 1.2.3 0000100002000030000
+// 2.2.0-a.1 0000200001999990010
+// 3.0.0-b.2 0000299999999995020
+// 2.2.0-a.1.z 0000200001999990011
+//
+#define LIBCUTL_VERSION $libcutl.version.project_number$ULL
+#define LIBCUTL_VERSION_STR "$libcutl.version.project$"
+#define LIBCUTL_VERSION_ID "$libcutl.version.project_id$"
+
+#define LIBCUTL_VERSION_MAJOR $libcutl.version.major$
+#define LIBCUTL_VERSION_MINOR $libcutl.version.minor$
+#define LIBCUTL_VERSION_PATCH $libcutl.version.patch$
+
+#define LIBCUTL_PRE_RELEASE $libcutl.version.pre_release$
+
+#define LIBCUTL_SNAPSHOT $libcutl.version.snapshot_sn$ULL
+#define LIBCUTL_SNAPSHOT_ID "$libcutl.version.snapshot_id$"
+
+#endif // LIBCUTL_VERSION