Skip to content

Commit

Permalink
use iterators for split (#14)
Browse files Browse the repository at this point in the history
* use iterators for split

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* address review comments

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
  • Loading branch information
Karsten1987 authored and emersonknapp committed Jun 13, 2019
1 parent 1760a4f commit 398d5bf
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 6 deletions.
35 changes: 29 additions & 6 deletions include/rcpputils/split.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,57 @@
#ifndef RCPPUTILS__SPLIT_HPP_
#define RCPPUTILS__SPLIT_HPP_

#include <iterator>
#include <sstream>
#include <string>
#include <vector>

namespace rcpputils
{

/// Split a specified input into tokens using a delimiter.
/// Split a specified input into tokens using a delimiter and a type erased insert iterator.
/**
* The returned vector will contain the tokens split from the input
*
* \param[in] input the input string to be split
* \param[in] delim the dlelimiter used to split the input string
* \return A vector of tokens.
* \param[in] insert iterator pointing to a storage container
*/
inline std::vector<std::string>
split(const std::string & input, char delim, bool skip_empty = false)
template<
class InsertIterator,
typename std::enable_if<
std::is_same<
InsertIterator &,
decltype(std::declval<InsertIterator>().operator=(std::declval<std::string>()))>::value
>::type * = nullptr>
void
split(const std::string & input, char delim, InsertIterator & it, bool skip_empty = false)
{
std::vector<std::string> result;
std::stringstream ss;
ss.str(input);
std::string item;
while (std::getline(ss, item, delim)) {
if (skip_empty && item == "") {
continue;
}
result.push_back(item);
it = item;
}
}

/// Split a specified input into tokens using a delimiter.
/**
* The returned vector will contain the tokens split from the input
*
* \param[in] input the input string to be split
* \param[in] delim the dlelimiter used to split the input string
* \return A vector of tokens.
*/
inline std::vector<std::string>
split(const std::string & input, char delim, bool skip_empty = false)
{
std::vector<std::string> result;
auto it = std::back_inserter(result);
split(input, delim, it, skip_empty);
return result;
}
} // namespace rcpputils
Expand Down
93 changes: 93 additions & 0 deletions test/test_split.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

#include <gtest/gtest.h>

#include <exception>
#include <list>
#include <set>
#include <string>
#include <tuple>
#include <vector>

#include "rcpputils/split.hpp"

TEST(test_split, split) {
Expand Down Expand Up @@ -221,3 +228,89 @@ TEST(test_split, split_backslash) {
EXPECT_EQ("world", ret[5]);
}
}

TEST(test_split, vector_iterator)
{
std::string s = "my/hello/world";

std::vector<std::string> vec = {};
auto vec_it = std::back_inserter(vec);
rcpputils::split(s, '/', vec_it);
EXPECT_EQ("my", vec[0]);
EXPECT_EQ("hello", vec[1]);
EXPECT_EQ("world", vec[2]);
}

TEST(test_split, list_iterator)
{
std::string s = "my/hello/world";

std::list<std::string> ll = {};
auto ll_it = std::back_inserter(ll);
rcpputils::split(s, '/', ll_it);
EXPECT_EQ("my", ll.front());
EXPECT_EQ("world", ll.back());
}

TEST(test_split, set_iterator)
{
std::string s = "wonderful string with wonderful duplicates duplicates";

std::set<std::string> set = {};
auto set_it = std::inserter(set, std::begin(set));
rcpputils::split(s, ' ', set_it);
EXPECT_EQ(4u, set.size());
}

class TripleExtractor
{
size_t counter = 0;

public:
std::string package_name;
std::string middle_element;
std::string message_name;

TripleExtractor & operator=(const std::string & s)
{
if (counter >= 3) {
throw std::out_of_range("triple extractor only can hold three parts");
}

if (counter == 0) {
package_name = s;
} else if (counter == 1) {
middle_element = s;
} else {
message_name = s;
}

counter++;

return *this;
}

std::tuple<std::string, std::string, std::string> get() const
{
return std::tie(package_name, middle_element, message_name);
}
};

TEST(test_split, custom_iterator)
{
std::string s = "my_pkg/msg/MyMessage";
TripleExtractor triple_it;
rcpputils::split(s, '/', triple_it);
EXPECT_STREQ("my_pkg", triple_it.package_name.c_str());
EXPECT_STREQ("msg", triple_it.middle_element.c_str());
EXPECT_STREQ("MyMessage", triple_it.message_name.c_str());

EXPECT_EQ(std::make_tuple("my_pkg", "msg", "MyMessage"), triple_it.get());
}

TEST(test_split, custom_iterator_exception)
{
std::string s = "my_pkg/msg/MyMessage/Wrong";
TripleExtractor triple_it;
ASSERT_THROW(rcpputils::split(s, '/', triple_it), std::out_of_range);
}

0 comments on commit 398d5bf

Please sign in to comment.