Skip to content

Commit

Permalink
Updated join with unittest
Browse files Browse the repository at this point in the history
  • Loading branch information
mmertama committed Dec 11, 2024
1 parent b9fe8f8 commit bdf5b3b
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 19 deletions.
2 changes: 1 addition & 1 deletion gempyrelib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
92 changes: 77 additions & 15 deletions gempyrelib/include/gempyre_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,23 @@ std::optional<T> parse(std::string_view source) {
template <class T>
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 <class T>
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
Expand Down Expand Up @@ -538,20 +550,73 @@ std::vector<K> keys(const T& map) {
return ks;
}

/// @cond INTERNAL
template<typename V>
struct DefaultJoiner {
auto operator()(const V& v) const {return v;}
};
/// @endcond

/// @cond INTERNAL
template <class IT, typename In, typename Out>
[[deprecated("See join with Callable")]] std::string join(const IT& begin,
const IT& end,
std::string_view joinChar = "",
const std::function<Out (const In&)>& 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 <class T, typename In, typename Out>
[[deprecated("See join with Callable")]] std::string join(const T& t,
std::string_view joinChar = "",
const std::function<Out (const In&)>& f = [](const In& v)->Out{return v;}) {
return join(t.begin(), t.end(), joinChar, f);
}

template <class IT, typename In, typename Out, typename = std::enable_if_t<std::is_pointer<IT>::value>>
[[deprecated("See join with Callable")]] std::string join(const IT begin,
const IT end,
const std::string joinChar = "",
const std::function<Out (const In&)>& 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 <class IT, typename In=typename IT::value_type, typename Out=typename IT::value_type>
template <typename IT, typename Callable = DefaultJoiner<typename IT::value_type>,
typename = std::enable_if_t<!std::is_pointer<IT>::value>>
std::string join(const IT& begin,
const IT& end,
std::string_view joinChar = "",
const std::function<Out (const In&)>& f = [](const In& k)->Out{return k;}) {
const Callable& f = Callable{}) {
std::string s;
std::ostringstream iss(s);
if(begin != end) {
Expand All @@ -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 <class T, typename In=typename T::value_type, typename Out=typename T::value_type>
template <class T, typename Callable = DefaultJoiner<typename T::value_type>>
std::string join(const T& t,
std::string_view joinChar = "",
const std::function<Out (const In&)>& 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 <class IT, typename In=typename std::remove_pointer<IT>::type,
typename Out=typename std::remove_pointer<IT>::type,
template <class IT, typename Callable = DefaultJoiner<typename std::remove_pointer<IT>::type>,
typename = std::enable_if_t<std::is_pointer<IT>::value>>
std::string join(const IT begin,
const IT end,
const std::string joinChar = "",
const std::function<Out (const In&)>& f = [](const In& k)->Out{return k;}) {
const Callable& f = Callable{}) {
std::string s;
std::ostringstream iss(s);
if(begin != end) {
Expand Down
4 changes: 2 additions & 2 deletions gempyrelib/src/appui/ui/gempyre_internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_t, map_t::value_type, std::string>(map, ",",
return '{' + GempyreUtils::join(map, ",",
[](const auto& kv){return kv.first + ':' + kv.second;}) + '}';
} else if(js.is_array()) {
Server::Array array;
Expand Down Expand Up @@ -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<void(int)> GempyreInternal::makeCaller(const std::function<void (Ui::TimerId id)>& function) {
const auto caller = [this, function](int id) {
auto call = [function, id]() {
Expand Down
2 changes: 1 addition & 1 deletion gempyrelib/src/uwebsockets/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ void Uws_Server::serverThread(unsigned int port) {
"Uri:", url,
"query:", req->getQuery(),
"path:", fullPath,
"header:", GempyreUtils::join<uWS::HttpRequest::HeaderIterator, std::pair<std::string_view, std::string_view>, std::string>(
"header:", GempyreUtils::join(
req->begin(),
req->end(),
",",
Expand Down
31 changes: 31 additions & 0 deletions test/unittests/unittests.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <thread>
#include <sstream>
#include <gtest/gtest.h>
#include "gempyre_utils.h"
#include "gempyre.h"
#include "gempyre_graphics.h"
#include "timequeue.h"
Expand Down Expand Up @@ -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<int> 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<int> 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<int> 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)
Expand Down

0 comments on commit bdf5b3b

Please sign in to comment.