From: Philipp Gesang Date: Thu, 19 Jul 2018 10:54:06 +0000 (+0200) Subject: implement iterator based join_string() X-Git-Tag: v2.10~6^2~2 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=5ad803814e46f390e81cf7f4a9729b91c18fe25c;p=libi2ncommon implement iterator based join_string() For more idiomatic C++, add a version of join_string () that accepts a pair of iterators and templatize it, to supersede the existing boilerplate variants. Good riddance. --- diff --git a/src/stringfunc.cpp b/src/stringfunc.cpp index a8c501b..d611abf 100644 --- a/src/stringfunc.cpp +++ b/src/stringfunc.cpp @@ -430,78 +430,6 @@ std::list split_string( } // eo split_string(const std::string&,const std::string&,bool,const std::string&) -/** - * @brief joins a list of strings into a single string. - * - * This funtion is (basically) the reverse operation of @a split_string. - * - * @param parts the list of strings. - * @param delimiter the delimiter which is inserted between the strings. - * @return the joined string. - */ -std::string join_string( - const std::list< std::string >& parts, - const std::string& delimiter -) -{ - std::string result; - if (! parts.empty() ) - { - std::list< std::string >::const_iterator it= parts.begin(); - result = *it; - while ( ++it != parts.end() ) - { - result+= delimiter; - result+= *it; - } - } - return result; -} // eo join_string(const std::list< std::string >&,const std::string&) - - -/** @brief same as join_string for list, except uses a vector */ -std::string join_string( - const std::vector< std::string >& parts, - const std::string& delimiter -) -{ - std::string result; - if (! parts.empty() ) - { - std::vector< std::string >::const_iterator it= parts.begin(); - result = *it; - while ( ++it != parts.end() ) - { - result+= delimiter; - result+= *it; - } - } - return result; -} // eo join_string(const std::vector< std::string >&,const std::string&) - -/** @brief same as join_string for list, except uses a set */ -std::string join_string( - const std::set< std::string >& parts, - const std::string& delimiter -) -{ - std::string result; - - if (! parts.empty() ) - { - BOOST_FOREACH(const std::string &part, parts) - { - if (!result.empty ()) - { - result += delimiter; - } - result += part; - } - } - - return result; -} // eo join_string(const std::vector< std::string >&,const std::string&) - std::string join_string ( const char *const parts[], /* assumed NULL-terminated */ const std::string& delimiter diff --git a/src/stringfunc.hxx b/src/stringfunc.hxx index 29b8aba..bb5a960 100644 --- a/src/stringfunc.hxx +++ b/src/stringfunc.hxx @@ -34,7 +34,9 @@ on this file might be covered by the GNU General Public License. #ifndef __STRINGFUNC_HXX #define __STRINGFUNC_HXX +#include #include +#include #include #include #include @@ -130,21 +132,46 @@ std::list< std::string > split_string( const std::string& trim_list= std::string() ); +struct concatenator { + std::string delim; -std::string join_string( - const std::list< std::string >& parts, - const std::string& delimiter = "\n" -); + concatenator (const std::string &delim) : delim (delim) { } -std::string join_string( - const std::vector< std::string >& parts, - const std::string& delimiter = "\n" -); + inline std::string operator() (const std::string &acc, const std::string &elt) const + { return acc + delim + elt; } +}; -std::string join_string( - const std::set< std::string >& parts, +template +std::string +join_string ( + Iter first, + Iter last, + const std::string &delimiter = "\n" +) +{ + if (first == last) { return ""; } + + const std::string &init = *first++; + if (first == last) { return init; } + + return std::accumulate (first, last, init, concatenator (delimiter)); +} + +/** + * @brief joins a container of strings into a single string. + * + * This funtion is (basically) the reverse operation of @a split_string. + * + * @param parts the container of strings. + * @param delimiter the delimiter to insert between the strings. + * @return the joined string. + */ +template +inline std::string join_string( + const Cont& parts, const std::string& delimiter = "\n" -); +) +{ return join_string (parts.begin (), parts.end (), delimiter); } std::string join_string( const char *const parts [], diff --git a/test/stringfunc.cpp b/test/stringfunc.cpp index 38c8e71..7771b55 100644 --- a/test/stringfunc.cpp +++ b/test/stringfunc.cpp @@ -614,9 +614,17 @@ BOOST_AUTO_TEST_CASE(SplitToVector) BOOST_AUTO_TEST_CASE(JoinString1) { std::list< std::string > parts; - get_push_back_filler(parts)("1")("2")("drei"); - std::string joined_string= join_string(parts,"/"); + BOOST_CHECK_EQUAL( std::string("") , joined_string ); + + parts.push_back ("1"); + joined_string= join_string(parts,"/"); + // we should have slashes between the strings: + BOOST_CHECK_EQUAL( std::string("1") , joined_string ); + + get_push_back_filler(parts)("2")("drei"); + + joined_string= join_string(parts,"/"); // we should have slashes between the strings: BOOST_CHECK_EQUAL( std::string("1/2/drei") , joined_string ); @@ -655,6 +663,70 @@ BOOST_AUTO_TEST_CASE(JoinStringVector) } // eo JoinStringVector +BOOST_AUTO_TEST_CASE(JoinStringSet) +{ + std::set< std::string > parts; + + std::string joined_string= join_string(parts,"/"); + BOOST_CHECK_EQUAL( std::string() , joined_string ); + + parts.insert ("foo"); + joined_string= join_string(parts,"/"); + BOOST_CHECK_EQUAL( std::string("foo") , joined_string ); + + parts.insert ("bar"); + parts.insert ("baz"); + + joined_string= join_string(parts,"/"); + // we should have slashes between the strings: + BOOST_CHECK_EQUAL( std::string("bar/baz/foo") , joined_string ); + + parts.insert( std::string() ); + joined_string= join_string(parts,"/"); + // now we should have an additional trailing slash: + BOOST_CHECK_EQUAL( std::string("/bar/baz/foo") , joined_string ); +} // eo JoinStringSet + + +BOOST_AUTO_TEST_CASE(JoinStringIterSet_Empty) +{ + std::set< std::string > parts; + + // empty sequence → empty string + BOOST_CHECK_EQUAL(join_string (parts.begin (), parts.end () ), ""); + BOOST_CHECK_EQUAL(join_string (parts.begin (), parts.end (), "/"), ""); +} // eo JoinStringSet + +BOOST_AUTO_TEST_CASE(JoinStringIterSet_One) +{ + std::set< std::string > parts; + + parts.insert ("foo"); + + // cardinality == 1 → no delimiter + BOOST_CHECK_EQUAL(join_string (parts.begin (), parts.end () ), "foo"); + BOOST_CHECK_EQUAL(join_string (parts.begin (), parts.end (), "/"), "foo"); +} // eo JoinStringSet + +BOOST_AUTO_TEST_CASE(JoinStringIterSet) +{ + std::set< std::string > parts; + + parts.insert ("foo"); + parts.insert ("bar"); + parts.insert ("baz"); + + std::string joined_string= join_string(parts.begin (), parts.end (), "/"); + // we should have slashes between the strings: + BOOST_CHECK_EQUAL( std::string("bar/baz/foo") , joined_string ); + + parts.insert( std::string() ); + joined_string= join_string(parts.begin (), parts.end (),"/"); + // now we should have an additional trailing slash: + BOOST_CHECK_EQUAL( std::string("/bar/baz/foo") , joined_string ); +} // eo JoinStringSet + + BOOST_AUTO_TEST_CASE(ConversionStringInt) { std::string s1("24");