String family unification

I would like to write a function that combines any std::string sequence with just one malloc behind the scene. As a result, you must first calculate the total length of the string. The function should be used this way:

 std::string s0 = ...; std::string s1 = ...; std::string s2 = ...; std::string s = join(s0, s1, s2); 

Better join would use a combination of std::string and std::string_view . It would be even better if we could add string literals. How could you write such a function in C ++ 11 (you need to compile it with gcc 4.8.5 and Visual Studio 2015 )?

+5
source share
5 answers

If you have a string_view type, you can just take the string_view s collection, sum their sizes, select, and then copy.

 std::string join(std::initializer_list<string_view> values) { std::string result; result.reserve(std::accumulate(values.begin(), values.end(), 0, [](string_view s) { return s.length(); })); std::for_each(values.begin(), values.end(), [&result](string_view s) { result.append(s.data()); }); return result; } 
+4
source

With range-v3 you can use concat :

 const std::string s = ranges::view::concat(s0, s1, " world\n"); 
+3
source

Here is a possible implementation:

 template<typename... Args> std::string join(const Args&... args) { size_t size = 0; for( const std::string& s : { args... } ) size += s.size(); std::string result; result.reserve(size); for( const std::string& s : { args... } ) result += s; return result; } 

Demo: https://wandbox.org/permlink/qwG0LMewsHwVuGXN

+2
source

Benoit almost had it, its implementation just needed the necessary boiler plate for processing various types of strings. To his credit, I must admit that this is quite a bit of a boiler room. :)

gcc -std=c++17 with gcc -std=c++17

 #include <string> #include <string.h> #include <string_view> #include <iostream> namespace detail { template<typename C> size_t myStrLen(const std::basic_string<C>& s) { return s.length(); } template<typename C> size_t myStrLen(const std::basic_string_view<C>& s) { return s.length(); } size_t myStrLen(char) { return 1; } size_t myStrLen(wchar_t) { return 1; } size_t myStrLen(const char* s) { return strlen(s); } size_t myStrLen(const wchar_t* s) { return wcslen(s); } template<typename T, typename...Args> size_t myStrLen(T&& t, Args&&...args) { return myStrLen(std::forward<T>(t)) + myStrLen(args...); } template<typename C, typename T> void myConcat(std::basic_string<C>& result, T&& t) { result += t; } template<typename C, typename T, typename...Args> void myConcat(std::basic_string<C>& result, T&& t, Args&&...args) { result += t; myConcat(result, args...); } } template<typename C, typename... Args> auto join(Args&&... args) { std::basic_string<C> result; result.reserve(detail::myStrLen(args...)); detail::myConcat(result, args...); return result; } template<typename... Args> auto join(Args&&... args) { return join<char>(args...); } template<typename... Args> auto wjoin(Args&&... args) { return join<wchar_t>(args...); } int main() { std::string str{"hello"}; std::string_view sv{"world"}; std::wstring wstr{L"hello"}; std::wstring_view wsv{L"world"}; std::cout << join(str, " ", sv, '\n'); std::wcout << wjoin(wstr, L" ", wsv, L'\n'); return 0; } 

[EDIT] Moved join<charType> template from the namespace area, as this may be useful for other template constructs.

+2
source

I believe this is possible using the ideal call forwarding argument and Variadic.

joinStr.hpp:

  class joinStr { public: template<typename... Args> joinStr(const std::string& first, Args&&... args): ret(""), size(0) { size = first.length(); getSize((args)...); std::cout << "before reserve : " << ret.capacity() << std::endl; ret.reserve(size); std::cout << "after reserve : " << ret.capacity() << std::endl; ret.append(first); join(std::forward<Args>(args)...); } ~joinStr() {} std::string getStr() { std::cout << ret << std::endl; return ret; } private: std::string ret; int size; int getSize() { std::cout << "size : " << size << std::endl; return size; } template<typename... Args> int getSize(const std::string& first, const Args&... args) { size += first.length(); return getSize((args)...); } template<typename... Args> int getSize(char* first, const Args&... args) { size += strlen(first); return getSize((args)...); } template<typename... Args> int getSize(const char* first, const Args&... args) { size += strlen(first); return getSize((args)...); } void join() { std::cout << "Final capacity : " << ret.capacity() << std::endl; } template<typename... Args> void join(const std::string& first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } template<typename... Args> void join(char* first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } template<typename... Args> void join(const char* first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } }; 

main:

 int main() { std::string s1 = "hello"; std::string s2 = "world !"; std::string s3 = "meatpopsicle"; const char* s4 = "__yeah__"; joinStr c(s1, s2, s3, s4); return 1; } 

Note that I wrapped it inside the class, but you can do the same outside the class.

As a bonus, it works with both std :: string, const char *, and char *.

Edit : fixed reuse of moved values ​​in getSize and added template for const char * after Caleth clause

Edit 2 : A version that allows passing the size_t field after const char * instead of using strlen.

  class joinStr { public: template<typename... Args> joinStr(const std::string& first, Args&&... args): ret(""), size(0) { size = first.length(); getSize(args...); std::cout << "before reserve : " << ret.capacity() << std::endl; ret.reserve(size); std::cout << "after reserve : " << ret.capacity() << std::endl; ret.append(first); join(std::forward<Args>(args)...); } ~joinStr() {} std::string getStr() { std::cout << ret << std::endl; return ret; } private: std::string ret; int size; int getSize() { std::cout << "size : " << size << std::endl; return size; } template<typename... Args> int getSize(const std::string& first, const Args&... args) { size += first.length(); return getSize((args)...); } // const char * template<typename... Args, typename T> int getSize(const char* first, const T& t, const Args&... args) { getSizeImpl<T>(first, t, std::integral_constant<bool, std::is_same<T, size_t>::value>()); return getSize(t, args...); } template<typename T> void getSizeImpl(const char* first, const T& t, std::false_type) { // Case when the next argument is not a size_t type size += strlen(first); } template<typename T> void getSizeImpl(const char* first,const T& t, std::true_type) { // Do nothing } template<typename... Args> int getSize(size_t s, const Args&... args) { size += s; std::cout << "size_t " << s << " in getSize" << std::endl; return getSize(args...); } void join() { std::cout << "Final capacity : " << ret.capacity() << std::endl; } template<typename... Args> void join(const std::string& first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } template<typename... Args> void join(char* first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } template<typename... Args> void join(const char* first, Args&&... args) { ret.append(first); join(std::forward<Args>(args)...); } template<typename... Args> void join(size_t s, Args&&... args) { join(std::forward<Args>(args)...); } }; 

main:

 int main() { std::string s1 = "hello"; std::string s2 = "world !"; std::string s3 = "meatpopsicle"; const char* s4 = "__yeah__"; joinStr c(s1, s2, s3, s4, (size_t)8); return 1; } 
+1
source

Source: https://habr.com/ru/post/1271221/


All Articles