aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-28 16:13:54 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-28 16:13:54 +0200
commit56ce5687567150b0b2cc3e57540d564793ef6bf7 (patch)
tree79b3cef1169b82f8d85459b285e2fc39f9d8a0e9
parent9d818423031083f227a5e872826ed8c2d6e14a0f (diff)
Add support for iteration over path components
-rw-r--r--build/path59
-rw-r--r--build/path.ixx35
-rw-r--r--tests/build/path/driver.cxx34
3 files changed, 128 insertions, 0 deletions
diff --git a/build/path b/build/path
index d153ed7..4c34a58 100644
--- a/build/path
+++ b/build/path
@@ -6,8 +6,10 @@
#define BUILD_PATH
#include <string>
+#include <cstddef> // ptrdiff_t
#include <ostream>
#include <utility> // move
+#include <iterator>
#include <exception>
#include <functional> // hash
@@ -352,6 +354,63 @@ namespace build
basic_path
relative (basic_path) const;
+ // Iteration over path components.
+ //
+ public:
+ struct iterator
+ {
+ typedef string_type value_type;
+ typedef string_type* pointer;
+ typedef string_type& reference;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::forward_iterator_tag iterator_category;
+
+ typedef typename string_type::size_type size_type;
+
+ iterator (): p_ (nullptr) {}
+ iterator (const string_type& p, size_type b, size_type e)
+ : p_ (&p), b_ (b), e_ (e) {}
+
+ iterator&
+ operator++ ()
+ {
+ b_ = e_;
+
+ if (b_ != string_type::npos)
+ e_ = traits::find_separator (*p_, ++b_);
+
+ return *this;
+ }
+
+ iterator
+ operator++ (int) {iterator r (*this); return ++r;}
+
+ string_type operator* () const
+ {
+ return string_type (*p_, b_, (e_ != string_type::npos ? e_ - b_ : e_));
+ }
+
+ friend bool
+ operator== (const iterator& x, const iterator& y)
+ {
+ return x.p_ == y.p_ && x.b_ == y.b_ && x.e_ == y.e_;
+ }
+
+ friend bool
+ operator!= (const iterator& x, const iterator& y) {return !(x == y);}
+
+ private:
+ // b != npos && e == npos - last component
+ // b == npos && e == npos - one past last component (end)
+ //
+ const string_type* p_;
+ size_type b_;
+ size_type e_;
+ };
+
+ iterator begin () const;
+ iterator end () const;
+
public:
// Normalize the path. This includes collapsing the '.' and '..'
// directories if possible, collapsing multiple directory
diff --git a/build/path.ixx b/build/path.ixx
index ff1ec32..8ee6a33 100644
--- a/build/path.ixx
+++ b/build/path.ixx
@@ -85,6 +85,41 @@ namespace build
}
template <typename C, typename K>
+ inline auto basic_path<C, K>::
+ begin () const -> iterator
+ {
+ size_type b, e;
+
+ if (this->path_.empty ())
+ b = e = string_type::npos;
+
+#ifndef _WIN32
+ else if (root ())
+ {
+ // We want to return a single empty component. Here we return
+ // the begin position one past the end. Not sure if this legal.
+ //
+ b = 1;
+ e = string_type::npos;
+ }
+#endif
+ else
+ {
+ b = 0;
+ e = traits::find_separator (this->path_);
+ }
+
+ return iterator (this->path_, b, e);
+ }
+
+ template <typename C, typename K>
+ inline auto basic_path<C, K>::
+ end () const -> iterator
+ {
+ return iterator (this->path_, string_type::npos, string_type::npos);
+ }
+
+ template <typename C, typename K>
inline basic_path<C, K>& basic_path<C, K>::
complete ()
{
diff --git a/tests/build/path/driver.cxx b/tests/build/path/driver.cxx
index 739146b..3aa624c 100644
--- a/tests/build/path/driver.cxx
+++ b/tests/build/path/driver.cxx
@@ -77,6 +77,40 @@ main ()
assert (path ("C:\\foo.txt").base ().string () == "C:\\foo");
#endif
+ // iteration
+ //
+ {
+ path p;
+ assert (p.begin () == p.end ());
+ }
+ {
+ path p ("foo");
+ path::iterator i (p.begin ());
+ assert (i != p.end () && *i == "foo");
+ assert (++i == p.end ());
+ }
+ {
+ path p ("foo/bar");
+ path::iterator i (p.begin ());
+ assert (i != p.end () && *i == "foo");
+ assert (++i != p.end () && *i == "bar");
+ assert (++i == p.end ());
+ }
+ {
+ path p ("/foo/bar");
+ path::iterator i (p.begin ());
+ assert (i != p.end () && *i == "");
+ assert (++i != p.end () && *i == "foo");
+ assert (++i != p.end () && *i == "bar");
+ assert (++i == p.end ());
+ }
+ {
+ path p ("/");
+ path::iterator i (p.begin ());
+ assert (i != p.end () && *i == "");
+ assert (++i == p.end ());
+ }
+
// operator/
//
#ifndef _WIN32