From bdf5b3b7ef9c32829193e977b47aff461ff4cae6 Mon Sep 17 00:00:00 2001 From: Markus Mertama Date: Wed, 11 Dec 2024 15:49:23 +0200 Subject: [PATCH] Updated join with unittest --- gempyrelib/CMakeLists.txt | 2 +- gempyrelib/include/gempyre_utils.h | 92 ++++++++++++++++---- gempyrelib/src/appui/ui/gempyre_internal.cpp | 4 +- gempyrelib/src/uwebsockets/server.cpp | 2 +- test/unittests/unittests.cpp | 31 +++++++ 5 files changed, 112 insertions(+), 19 deletions(-) diff --git a/gempyrelib/CMakeLists.txt b/gempyrelib/CMakeLists.txt index 9ec1d1c1..60e120aa 100644 --- a/gempyrelib/CMakeLists.txt +++ b/gempyrelib/CMakeLists.txt @@ -1,7 +1,7 @@ #cmake_minimum_required (VERSION 3.25) -project (gempyre VERSION 1.6.2 LANGUAGES CXX C) +project (gempyre VERSION 1.6.3 LANGUAGES CXX C) # Must use GNUInstallDirs to install libraries into correct # locations on all platforms. include(GNUInstallDirs) diff --git a/gempyrelib/include/gempyre_utils.h b/gempyrelib/include/gempyre_utils.h index 7301ab48..c69e9774 100644 --- a/gempyrelib/include/gempyre_utils.h +++ b/gempyrelib/include/gempyre_utils.h @@ -423,11 +423,23 @@ std::optional parse(std::string_view source) { template std::string to_low(const T& str) { std::string n; - std::transform(str.begin(), str.end(), std::back_inserter(n), + std::transform(std::begin(str), std::end(str), std::back_inserter(n), [](auto c){return std::tolower(c);}); return n; } + +/// @brief make upper case string +/// @param str +/// @return string +template +std::string to_upper(const T& str) { + std::string n; + std::transform(std::begin(str), std::end(str), std::back_inserter(n), + [](auto c){return std::toupper(c);}); + return n; +} + /// @brief trim from left /// @param str @@ -538,20 +550,73 @@ std::vector keys(const T& map) { return ks; } +/// @cond INTERNAL + template + struct DefaultJoiner { + auto operator()(const V& v) const {return v;} + }; +/// @endcond + +/// @cond INTERNAL +template +[[deprecated("See join with Callable")]] std::string join(const IT& begin, + const IT& end, + std::string_view joinChar = "", + const std::function& f = [](const In& k)->Out{return k;}) { + std::string s; + std::ostringstream iss(s); + if(begin != end) { + for(auto it = begin;;) { + iss << f(*it); + if(!(++it != end)) break; + if(!joinChar.empty()) + iss << joinChar; + } + } + return iss.str(); +} + +template +[[deprecated("See join with Callable")]] std::string join(const T& t, + std::string_view joinChar = "", + const std::function& f = [](const In& v)->Out{return v;}) { + return join(t.begin(), t.end(), joinChar, f); +} + +template ::value>> +[[deprecated("See join with Callable")]] std::string join(const IT begin, + const IT end, + const std::string joinChar = "", + const std::function& f = [](const In& k)->Out{return k;}) { + std::string s; + std::ostringstream iss(s); + if(begin != end) { + for(auto it = begin;;) { + iss << f(*it); + if(!(++it != end)) break; + if(!joinChar.empty()) + iss << joinChar; + } + } + return iss.str(); +} + +/// @endcond + /// @brief Join container values, try 1st if compiler can deduct types /// @tparam IT container -/// @tparam In default container value type -/// @tparam Out default to container type +/// @tparam Callable conversion /// @param begin begin iterator /// @param end end iterator /// @param joinChar optional glue string -/// @param f optional transform function +/// @param f optional transform function /// @return string -template +template , + typename = std::enable_if_t::value>> std::string join(const IT& begin, const IT& end, std::string_view joinChar = "", - const std::function& f = [](const In& k)->Out{return k;}) { + const Callable& f = Callable{}) { std::string s; std::ostringstream iss(s); if(begin != end) { @@ -567,35 +632,32 @@ std::string join(const IT& begin, /// @brief Join container values, try 1st if compiler can deduct types /// @tparam IT container -/// @tparam In default container value type -/// @tparam Out default to container type +/// @tparam Callable conversion /// @param t container /// @param joinChar optional glue string /// @param f optional transform function /// @return string -template +template > std::string join(const T& t, std::string_view joinChar = "", - const std::function& f = [](const In& v)->Out{return v;}) { + const Callable& f = Callable{}) { return join(t.begin(), t.end(), joinChar, f); } /// @brief Join container values, try 1st if compiler can deduct types /// @tparam IT container -/// @tparam In default container value type -/// @tparam Out default to container type +/// @tparam Callable conversion /// @param begin begin iterator /// @param end end iterator /// @param joinChar optional glue string /// @param f optional transform function /// @return string -template ::type, - typename Out=typename std::remove_pointer::type, +template ::type>, typename = std::enable_if_t::value>> std::string join(const IT begin, const IT end, const std::string joinChar = "", - const std::function& f = [](const In& k)->Out{return k;}) { + const Callable& f = Callable{}) { std::string s; std::ostringstream iss(s); if(begin != end) { diff --git a/gempyrelib/src/appui/ui/gempyre_internal.cpp b/gempyrelib/src/appui/ui/gempyre_internal.cpp index f4bc2a68..9aaee4e2 100644 --- a/gempyrelib/src/appui/ui/gempyre_internal.cpp +++ b/gempyrelib/src/appui/ui/gempyre_internal.cpp @@ -455,7 +455,7 @@ std::string GempyreInternal::to_string(const nlohmann::json& js) { for(const auto& [key, value] : js.items()) { map.emplace(key, to_string(value)); } - return '{' + GempyreUtils::join(map, ",", + return '{' + GempyreUtils::join(map, ",", [](const auto& kv){return kv.first + ':' + kv.second;}) + '}'; } else if(js.is_array()) { Server::Array array; @@ -622,7 +622,7 @@ void GempyreInternal::send(const DataPtr& data, bool droppable) { // timer elapses calls a function // that calls a function that adds a another function to a queue -// that function calls a the actual timer functio when on top +// that function calls a the actual timer function when on top std::function GempyreInternal::makeCaller(const std::function& function) { const auto caller = [this, function](int id) { auto call = [function, id]() { diff --git a/gempyrelib/src/uwebsockets/server.cpp b/gempyrelib/src/uwebsockets/server.cpp index 7a2b0a2f..49132834 100644 --- a/gempyrelib/src/uwebsockets/server.cpp +++ b/gempyrelib/src/uwebsockets/server.cpp @@ -223,7 +223,7 @@ void Uws_Server::serverThread(unsigned int port) { "Uri:", url, "query:", req->getQuery(), "path:", fullPath, - "header:", GempyreUtils::join, std::string>( + "header:", GempyreUtils::join( req->begin(), req->end(), ",", diff --git a/test/unittests/unittests.cpp b/test/unittests/unittests.cpp index 38bd18fb..7866c630 100644 --- a/test/unittests/unittests.cpp +++ b/test/unittests/unittests.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "gempyre_utils.h" #include "gempyre.h" #include "gempyre_graphics.h" #include "timequeue.h" @@ -558,6 +559,36 @@ TEST(Unittests, json_test_set4) { ASSERT_EQ(*js, R"({"animals":[{"cat":"meow","food":"fish"},{"dog":"bark","food":"bone"}]})"); } +TEST(Unittests, join1) { + const std::vector v{1,2,3,4,5}; + const auto joined = GempyreUtils::join(v, "-"); + EXPECT_EQ(joined, "1-2-3-4-5"); +} + +TEST(Unittests, join2) { + const std::vector v{1,2,3,4,5}; + const auto joined = GempyreUtils::join(v, "", [](auto p){return std::to_string(p + 1);}); + EXPECT_EQ(joined, "23456"); +} + +TEST(Unittests, join3) { + const std::vector v{1,2,3,4,5}; + const auto joined = GempyreUtils::join(std::next(v.begin()), std::prev(v.end()), " "); + EXPECT_EQ(joined, "2 3 4"); +} + +TEST(Unittests, join4) { + const char* strs[] = {"aa", "bee", "cee", "dee"}; + const auto joined = GempyreUtils::join(std::begin(strs), std::end(strs), "_"); + EXPECT_EQ(joined, "aa_bee_cee_dee"); +} + +TEST(Unittests, join5) { + const char* strs[] = {"aa", "bee", "cee", "dee"}; + const auto joined = GempyreUtils::join(std::begin(strs), std::end(strs), "_", [](const auto& s) {return GempyreUtils::to_upper(std::string_view{s});}); + EXPECT_EQ(joined, "AA_BEE_CEE_DEE"); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); for(int i = 1 ; i < argc; ++i)