diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2020-12-16 20:29:05 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2021-02-24 16:40:04 +0300 |
commit | 8e761289a2446367267c6c0d9a26e734f0f78306 (patch) | |
tree | fb495d8c18801f271d124ee48731f10df396ca89 /libcutl | |
parent | 4c8104756b92b9fa16b3a725e8a6aa620dfd606e (diff) |
Get rid of legacy build systems and rename cutl/ to libcutl/
Diffstat (limited to 'libcutl')
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 |