LCOV - code coverage report
Current view: top level - snapdev - join_strings.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 15 15 100.0 %
Date: 2022-07-09 19:51:09 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : #pragma once
      20             : 
      21             : // C++
      22             : //
      23             : #include    <algorithm>
      24             : #include    <array>
      25             : #include    <numeric>
      26             : #include    <string>
      27             : #include    <string_view>
      28             : 
      29             : 
      30             : 
      31             : namespace snapdev
      32             : {
      33             : 
      34             : /** \brief Transform a set of strings in a string.
      35             :  *
      36             :  * This function concatenate all the strings from a container adding a
      37             :  * separator in between each. In effect, it does:
      38             :  *
      39             :  * \code
      40             :  *      s1 + sep + s2 + sep + s3...
      41             :  * \endcode
      42             :  *
      43             :  * If you do not need a separator, you can use the std::accumulate() function
      44             :  * although it is going to be slower (i.e. it will do a lot of realloc() since
      45             :  * it does not know how long the final string is going to be.)
      46             :  *
      47             :  * \note
      48             :  * The separator is added whether the token being added is empty or not.
      49             :  * If you do not want to add separators between empty strings, make sure
      50             :  * to remove them from your container first.
      51             :  *
      52             :  * \param[in] tokens  The container of strings.
      53             :  * \param[in] separator  The separator to add between each string.
      54             :  *
      55             :  * \return the number of items in the resulting container.
      56             :  */
      57             : template<class ContainerT>
      58           4 : typename ContainerT::value_type join_strings(
      59             :         ContainerT const & tokens
      60             :       , typename ContainerT::value_type const & separator)
      61             : {
      62           4 :     typename ContainerT::value_type result;
      63             : 
      64             :     // we have a special case because we want to access the first
      65             :     // item (see tokens[0] below) to make the for_each() simpler.
      66             :     //
      67           4 :     if(!tokens.empty())
      68             :     {
      69             :         // calculate the final size, which is way faster than reallocating
      70             :         // over and over again in the 'result += string' below
      71             :         //
      72           3 :         size_t const total_size(std::accumulate(tokens.begin(), tokens.end(), separator.length() * (tokens.size() - 1),
      73           7 :                                 [](size_t & sum, typename ContainerT::value_type const & str)
      74             :                                 {
      75           7 :                                     return sum + str.length();
      76           7 :                                 }));
      77             : 
      78           3 :         result.reserve(total_size);
      79             : 
      80             :         // avoid special case in the loop
      81             :         // (i.e. no separator before the first token)
      82             :         //
      83           3 :         result += *tokens.begin();
      84             : 
      85           3 :         std::for_each(
      86             :                   std::next(tokens.begin())
      87             :                 , tokens.end()
      88           4 :                 , [&separator, &result](auto const & s)
      89           8 :                         {
      90          12 :                             result += separator + s;
      91           4 :                         });
      92             :     }
      93             : 
      94           4 :     return result;
      95             : }
      96             : 
      97             : 
      98             : namespace detail
      99             : {
     100             : 
     101             : /** \brief Join strings at compile time.
     102             :  *
     103             :  * Here is a template to concatate a set of `std::string_view`'s.
     104             :  *
     105             :  * If you have two or more string views that you want to join at compile
     106             :  * time, the join_string_views template uses this template and returns
     107             :  * an std::string_view.
     108             :  */
     109             : template<std::string_view const & ...strings>
     110             : struct join_string_views_impl
     111             : {
     112             :     // join all strings into a single std::array of chars
     113             :     //
     114             :     static constexpr auto concatenate() noexcept
     115             :     {
     116             :         constexpr std::size_t const len = (strings.size() + ... + 0);
     117             :         std::array<char, len + 1> arr{};
     118             :         auto append = [i = 0, &arr](auto const & s) mutable
     119             :         {
     120             :             for(auto c : s)
     121             :             {
     122             :                 arr[i] = c;
     123             :                 ++i;
     124             :             }
     125             :         };
     126             :         (append(strings), ...);
     127             :         arr[len] = 0;
     128             :         return arr;
     129             :     }
     130             : 
     131             :     // give the joined string static storage
     132             :     //
     133             :     static constexpr auto concatenated_strings = concatenate();
     134             : 
     135             :     // view as a std::string_view
     136             :     //
     137             :     static constexpr std::string_view value{
     138             :               concatenated_strings.data()
     139             :             , concatenated_strings.size() - 1
     140             :         };
     141             : };
     142             : 
     143             : /** \brief Join strings at compile time with a separator.
     144             :  *
     145             :  * This template is similar to the previous one, only it allows us to also
     146             :  * add a separator betweene each string.
     147             :  *
     148             :  * If you have two or more string views that you want to join with a
     149             :  * separator at compile time, the join_string_views_with_separator template
     150             :  * uses this template and returns an std::string_view.
     151             :  *
     152             :  * \tparam separator  The separator to add between each string
     153             :  * \tparam strings  The string views to concatanate
     154             :  */
     155             : template<
     156             :       std::string_view const & separator
     157             :     , std::string_view const & ...strings>
     158             : struct join_string_views_with_separator_impl
     159             : {
     160             :     // join all strings into a single std::array of chars
     161             :     //
     162             :     static constexpr auto concatenate() noexcept
     163             :     {
     164             :         constexpr std::size_t const count = sizeof...(strings); //std::tuple_size<std::tuple<strings...>>::value;
     165             :         constexpr std::size_t const len = (strings.size() + ... + 0) + separator.length() * (count - 1);
     166             :         std::array<char, len + 1> arr{};
     167             :         auto append = [i = 0, &arr](auto const & s) mutable
     168             :         {
     169             :             if(i != 0)
     170             :             {
     171             :                 for(auto c : separator)
     172             :                 {
     173             :                     arr[i] = c;
     174             :                     ++i;
     175             :                 }
     176             :             }
     177             :             for(auto c : s)
     178             :             {
     179             :                 arr[i] = c;
     180             :                 ++i;
     181             :             }
     182             :         };
     183             :         (append(strings), ...);
     184             :         arr[len] = 0;
     185             :         return arr;
     186             :     }
     187             : 
     188             :     // give the joined string static storage
     189             :     //
     190             :     static constexpr auto concatenated_strings = concatenate();
     191             : 
     192             :     // view as a std::string_view
     193             :     //
     194             :     static constexpr std::string_view value{
     195             :               concatenated_strings.data()
     196             :             , concatenated_strings.size() - 1
     197             :         };
     198             : };
     199             : 
     200             : } // namespace detail
     201             : 
     202             : 
     203             : /** \brief Join string views at compile time.
     204             :  *
     205             :  * Whenever you create a string view (i.e. a string literal in C++17 and newer
     206             :  * compilers), you can concatenate them using the join_string_views template.
     207             :  *
     208             :  * Here is an example on how to use this template:
     209             :  *
     210             :  * \code
     211             :  *     // various strings
     212             :  *     //
     213             :  *     constexpr std::string_view hello = "Hello";
     214             :  *     constexpr std::string_view space = " ";
     215             :  *     constexpr std::string_view world = "world";
     216             :  *     constexpr std::string_view bang = "!";
     217             :  *
     218             :  *     // concatenated strings
     219             :  *     //
     220             :  *     constexpr std::string_view hello_world = join_string_views<hello, space, world, bang>;
     221             :  * \endcode
     222             :  *
     223             :  * \source
     224             :  * https://stackoverflow.com/questions/38955940
     225             :  */
     226             : template<std::string_view const & ...strings>
     227             : static constexpr auto join_string_views = detail::join_string_views_impl<strings...>::value;
     228             : 
     229             : 
     230             : /** \brief Join string views with a separator at compile time.
     231             :  *
     232             :  * Whenever you create a string view (i.e. a string literal in C++17 and newer
     233             :  * compilers), you can concatenate them using the
     234             :  * join_string_views_with_separator template.
     235             :  *
     236             :  * Here is an example on how to use this template:
     237             :  *
     238             :  * \code
     239             :  *     // various strings
     240             :  *     //
     241             :  *     constexpr std::string_view space = " ";
     242             :  *     constexpr std::string_view hello = "Hello";
     243             :  *     constexpr std::string_view every = "every";
     244             :  *     constexpr std::string_view body = "body";
     245             :  *
     246             :  *     // concatenated strings
     247             :  *     //
     248             :  *     constexpr std::string_view hello_everyone = join_string_views<space, hello, every, body>;
     249             :  * \endcode
     250             :  */
     251             : template<std::string_view const & separator, std::string_view const & ...strings>
     252             : static constexpr auto join_string_views_with_separator = detail::join_string_views_with_separator_impl<separator, strings...>::value;
     253             : 
     254             : 
     255             : } // namespace snapdev
     256             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13