diff --git a/include/boost/algorithm/string/detail/formatter.hpp b/include/boost/algorithm/string/detail/formatter.hpp index f4c6728b3..c6135f465 100644 --- a/include/boost/algorithm/string/detail/formatter.hpp +++ b/include/boost/algorithm/string/detail/formatter.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -111,6 +112,28 @@ namespace boost { FinderT m_Finder; }; +// joint format functor ----------------------------------------------------// + + // joint format functor + template + struct join_formatF + { + public: + // Construction + join_formatF(const RangeT& Other) : + m_Other(Other) {} + + template + inline joined_range< + const Range2T, const RangeT> + operator()(const Range2T& Replace) const + { + return ::boost::range::join(Replace, m_Other); + } + + private: + RangeT m_Other; + }; } // namespace detail } // namespace algorithm diff --git a/include/boost/algorithm/string/detail/intersperse.hpp b/include/boost/algorithm/string/detail/intersperse.hpp new file mode 100644 index 000000000..041dbcaa0 --- /dev/null +++ b/include/boost/algorithm/string/detail/intersperse.hpp @@ -0,0 +1,56 @@ +// Boost string_algo library intersperse.hpp header file ---------------------------// + +// Copyright Denis Mikhailov 2022. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for updates, documentation, and revision history. + +#ifndef BOOST_STRING_INTERSPERSE_DETAIL_HPP +#define BOOST_STRING_INTERSPERSE_DETAIL_HPP + +#include +#include + +#include +#include +#include // for iterator_adaptor + +namespace boost { + namespace algorithm { + namespace detail { + + // just workaround for https://github.com/boostorg/iterator/issues/75 + // FIXME remove this when the issue will be resolved + template + struct just_iterator_adaptor : iterator_adaptor, It> { + explicit just_iterator_adaptor(It p) + : just_iterator_adaptor::iterator_adaptor_(p) {} + }; + + // functor to be passed into ::boost::token_finder + template + class non_last_condition { + public: + typedef BOOST_STRING_TYPENAME range_size::type size_type; + typedef BOOST_STRING_TYPENAME range_value::type value_type; + + explicit non_last_condition(size_type s, size_type& counter) + : m_Size(s), m_Counter(counter) { } + bool operator() (const value_type&) const { + return ++m_Counter < m_Size; + } + private: + const size_type m_Size; + size_type& m_Counter; + }; + + + } // namespace detail + } // namespace algorithm +} // namespace boost + + +#endif // BOOST_STRING_INTERSPERSE_DETAIL_HPP diff --git a/include/boost/algorithm/string/formatter.hpp b/include/boost/algorithm/string/formatter.hpp index 0e08ae7b3..f29eb7cd2 100644 --- a/include/boost/algorithm/string/formatter.hpp +++ b/include/boost/algorithm/string/formatter.hpp @@ -104,6 +104,26 @@ namespace boost { return detail::dissect_formatF(Finder); } + //! Join formatter + /*! + Constructs a \c join_formatter. Join formatter uses an other range to concatenate parameter + with the range. The joining of parameter and the range is returned + as a result + + \param Other a range used to be concatenated with parameter + \return An instance of the \c join_formatter object. + */ + template + inline detail::join_formatF< + iterator_range< + BOOST_STRING_TYPENAME range_const_iterator::type> > + join_formatter(const RangeT& Other) + { + return detail::join_formatF< + iterator_range< + BOOST_STRING_TYPENAME range_const_iterator::type> >(::boost::as_literal(Other)); + } + } // namespace algorithm @@ -112,6 +132,7 @@ namespace boost { using algorithm::identity_formatter; using algorithm::empty_formatter; using algorithm::dissect_formatter; + using algorithm::join_formatter; } // namespace boost diff --git a/include/boost/algorithm/string/intersperse.hpp b/include/boost/algorithm/string/intersperse.hpp new file mode 100644 index 000000000..3f5b7a126 --- /dev/null +++ b/include/boost/algorithm/string/intersperse.hpp @@ -0,0 +1,182 @@ +// Boost string_algo library intersperse.hpp header file ---------------------------// + +// Copyright Denis Mikhailov 2022. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for updates, documentation, and revision history. + +#ifndef BOOST_STRING_INTERSPERSE_HPP +#define BOOST_STRING_INTERSPERSE_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*! \file + Defines intersperse algorithms. + Intersperse algorithms are used to insert value between each contiguous elements + from a sequence (string). Also, variants with (\c _fill) postfix can insert a whole + range instead of value. + + Parametric (\c _generate) variants use a generator (functor) to obtain characters + to be inserted.. + Functions take a nullary function object as a parameter, which is used to extract + a new character and then insert. +*/ + +namespace boost { + namespace algorithm { + + // intersperse fill -----------------------------------------------// + + + //! Intersperse fill + /*! + Insert string between each contiguous elements from the input. + The input sequence is modified in-place. + + \param Input An input sequence + \param Fill A string to be inserted + */ + template + inline void intersperse_fill(SequenceT& Input, const RangeT& Fill) + { + BOOST_STRING_TYPENAME ::boost::range_size::type counter = 0; + ::boost::find_format_all( + Input, + ::boost::token_finder( + detail::non_last_condition(::boost::size(Input), counter)), + ::boost::join_formatter(::boost::as_literal(Fill))); + } + + //! Intersperse fill + /*! + Insert string between each contiguous elements from the input. + The result is an interspersed copy of the input + + \param Input An input sequence + \param Fill A string to be inserted + \return An interspersed copy of the input + + \note This function provides the strong exception-safety guarantee + */ + template + inline SequenceT intersperse_fill_copy( + const SequenceT& Input, + const RangeT& Fill) + { + BOOST_STRING_TYPENAME ::boost::range_size::type counter = 0; + return + ::boost::find_format_all_copy( + Input, + ::boost::token_finder( + detail::non_last_condition(::boost::size(Input), counter)), + ::boost::join_formatter(::boost::as_literal(Fill))); + } + + // intersperse -----------------------------------------------// + + //! Intersperse - parametric + /*! + Insert generated char between each contiguous elements from the input. + The input sequence is modified in-place. + + \param Input An input sequence + \param G A generator(nullary function object) used to create insertable char + */ + template + inline void intersperse_generate(SequenceT& Input, GeneratorT G) + { + typedef ::boost::function_input_iterator It; + intersperse_fill(Input, boost::make_iterator_range( + detail::just_iterator_adaptor(::boost::make_function_input_iterator(G, 0)), + detail::just_iterator_adaptor(::boost::make_function_input_iterator(G, 1)))); + } + + //! Intersperse - parametric + /*! + Insert generated char between each contiguous elements from the input. + The result is an interspersed copy of the input. + \param Input An input sequence + \param G A generator(nullary function object) used to create insertable char + \return An interspersed copy of the input + */ + template + inline SequenceT intersperse_generate_copy( + const SequenceT& Input, + GeneratorT G) + { + typedef ::boost::function_input_iterator It; + return + intersperse_fill_copy(Input, ::boost::make_iterator_range( + detail::just_iterator_adaptor(::boost::make_function_input_iterator(G, 0)), + detail::just_iterator_adaptor(::boost::make_function_input_iterator(G, 1)))); + } + + //! Intersperse + /*! + Insert char between each contiguous elements from the input. + The input sequence is modified in-place. + + \param Input An input sequence + \param Value A char to be inserted + */ + template + inline void intersperse(SequenceT& Input, const T& Value) + { + intersperse_fill(Input, ::boost::make_iterator_range( + ::boost::addressof(Value), + ::boost::addressof(Value)+1)); + } + + //! Intersperse + /*! + Insert char between each contiguous elements from the input. + The result is an interspersed copy of the input + + \param Input An input sequence + \param Value A char to be inserted + \return An interspersed copy of the input + + \note This function provides the strong exception-safety guarantee + */ + template + inline SequenceT intersperse_copy( + const SequenceT& Input, + const T& Value) + { + return + intersperse_fill_copy(Input, ::boost::make_iterator_range( + ::boost::addressof(Value), + ::boost::addressof(Value)+1)); + } + + } // namespace algorithm + + // pull names to the boost namespace + using algorithm::intersperse_fill; + using algorithm::intersperse_fill_copy; + using algorithm::intersperse_generate; + using algorithm::intersperse_generate_copy; + using algorithm::intersperse; + using algorithm::intersperse_copy; + +} // namespace boost + +#endif // BOOST_STRING_INTERSPERSE_HPP diff --git a/string/test/Jamfile.v2 b/string/test/Jamfile.v2 index 7f60df703..a6ff5ad9d 100644 --- a/string/test/Jamfile.v2 +++ b/string/test/Jamfile.v2 @@ -21,6 +21,12 @@ test-suite algorithm/string : : trim ] + [ run + intersperse_test.cpp unit_test_framework + : : + : + : intersperse + ] [ run conv_test.cpp unit_test_framework : : diff --git a/string/test/intersperse_test.cpp b/string/test/intersperse_test.cpp new file mode 100644 index 000000000..d56699832 --- /dev/null +++ b/string/test/intersperse_test.cpp @@ -0,0 +1,105 @@ +// Boost string_algo library intersperse_test.cpp file ---------------------------// + +// Copyright Denis Mikhailov 2022. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#include + +// Include unit test framework +#define BOOST_TEST_MAIN +#include + +#include +#include +#include + +using namespace std; +using namespace boost; + +struct CharGenerator { + typedef char result_type; + char counter; + CharGenerator() { counter = '1'; } + char operator() () { + return counter++; + } +}; + +void intersperse_test() +{ + string test = ""; + intersperse(test, ','); + BOOST_CHECK(test == ""); + + test = "a"; + intersperse(test, ','); + BOOST_CHECK(test == "a"); + + test = "ab"; + intersperse(test, ','); + BOOST_CHECK(test == "a,b"); + intersperse(test, ' '); + BOOST_CHECK(test == "a , b"); + intersperse(test, ' '); + BOOST_CHECK(test == "a , b"); + + const string input1 = "abcde"; + const string input2 = ""; + const string input3 = "a"; + const string input4 = "ab"; + + BOOST_CHECK(intersperse_copy(input1, ',') == "a,b,c,d,e"); + BOOST_CHECK(intersperse_copy(input2, ',') == ""); + BOOST_CHECK(intersperse_copy(input3, ',') == "a"); + BOOST_CHECK(intersperse_copy(input4, ',') == "a,b"); +} + +void intersperse_fill_test() +{ + string test = ""; + intersperse_fill(test, ", "); + BOOST_CHECK(test == ""); + + test = "a"; + intersperse_fill(test, ", "); + BOOST_CHECK(test == "a"); + + test = "ab"; + intersperse_fill(test, ", "); + BOOST_CHECK(test == "a, b"); + intersperse_fill(test, " "); + BOOST_CHECK(test == "a , b"); + + const string input1 = "abcde"; + const string input2 = ""; + const string input3 = "a"; + const string input4 = "ab"; + + BOOST_CHECK(intersperse_fill_copy(input1, ", ") == "a, b, c, d, e"); + BOOST_CHECK(intersperse_fill_copy(input2, ", ") == ""); + BOOST_CHECK(intersperse_fill_copy(input3, ", ") == "a"); + BOOST_CHECK(intersperse_fill_copy(input4, ", ") == "a, b"); +} + +void intersperse_generate_test() +{ + const string immutable_input = "test"; + CharGenerator g1, g2; + BOOST_CHECK(intersperse_generate_copy(immutable_input, g1) + == "t1e2s3t"); + + string mutable_input = "test"; + intersperse_generate(mutable_input, g2); + BOOST_CHECK(mutable_input == "t1e2s3t"); +} + +BOOST_AUTO_TEST_CASE( test_main ) +{ + intersperse_test(); + intersperse_fill_test(); + intersperse_generate_test(); +}