Browse Source

* improved string helper methods

- convert string to/from vector and list
    - added method to write object that support a certain 'to_string' method to a stream
master
bergmann 4 years ago
parent
commit
a22563e72d
2 changed files with 223 additions and 42 deletions
  1. +192
    -41
      include/cpputils/misc/string.h
  2. +31
    -1
      test/misc/string.cpp

+ 192
- 41
include/cpputils/misc/string.h View File

@@ -4,33 +4,129 @@
#include <cpputils/misc/exception.h>
#include <cpputils/misc/type_helper.h>

#ifdef _GLIBCXX_VECTOR
#define CPPUTILS_HAS_VECTOR
#endif

#ifdef _GLIBCXX_LIST
#define CPPUTILS_HAS_LIST
#endif

namespace utl
{

namespace __impl
{
template<class T, class Enable = void>
template<typename T, typename Enable = void>
struct op_to_string;

template<typename T, typename Enable = void>
struct op_from_string;
}

template<typename T>
inline void to_string(std::ostream& os, const T& t)
{ __impl::op_to_string<T>()(os, t); }

template<typename T>
inline std::string to_string(const T& t)
{
std::ostringstream ss;
to_string(ss, t);
return ss.str();
}

template<>
inline std::string to_string<std::string>(const std::string& s)
{ return s; }



template<typename T>
inline bool try_from_string(const std::string& s, T& value)
{ return __impl::op_from_string<T>()(s, value); }

template<typename T>
inline T from_string(const std::string& s, T defaultValue)
{
T tmp;
return try_from_string<T>(s, tmp)
? tmp
: defaultValue;
}

template<typename T>
inline T from_string(const std::string& s)
{
T tmp;
if (!try_from_string<T>(s, tmp))
throw exception(std::string("unable to convert string to ") + type_helper<T>::name() + ": " + s);
return tmp;
}

namespace __impl
{

/* op_to_string */

template<typename T, typename Enable>
struct op_to_string
{
inline void operator()(std::ostream& os, const T& s) const
{ os << s; }
};

template<class T>
template<typename T>
struct op_to_string<T, decltype(std::declval<T>().to_string(std::declval<std::ostream&>()), void())>
{
inline void operator()(std::ostream& os, const T& s) const
{ s.to_string(os); }
};

template<class T>
template<typename T>
struct op_to_string<T, decltype(std::declval<T>().to_string(), void())>
{
inline void operator()(std::ostream& os, const T& s) const
{ os << s.to_string(); }
};

template<class T, class Enable = void>
#ifdef CPPUTILS_HAS_VECTOR
template<typename T>
struct op_to_string<std::vector<T>, void>
{
inline void operator()(std::ostream& os, const std::vector<T>& v) const
{
bool first = true;
for (auto& x : v)
{
if (first) first = false;
else os << ",";
to_string(os, x);
}
}
};
#endif

#ifdef CPPUTILS_HAS_LIST
template<typename T>
struct op_to_string<std::list<T>, void>
{
inline void operator()(std::ostream& os, const std::list<T>& v) const
{
bool first = true;
for (auto& x : v)
{
if (first) first = false;
else os << ",";
to_string(os, x);
}
}
};
#endif

/* op_from_string */

template<typename T, typename Enable>
struct op_from_string
{
inline bool operator()(const std::string& s, T& value) const
@@ -61,21 +157,42 @@ namespace utl
}
};

template<class T>
template<>
struct op_from_string<bool, void>
{
inline bool operator()(const std::string& s, bool& value) const
{
static const std::string TRUE_STR("true");
int i;
value = s == "T"
|| s == "t"
|| std::equal(
s.begin(), s.end(),
TRUE_STR.begin(), TRUE_STR.end(),
[](char a, char b) {
return tolower(a) == tolower(b);
})
|| ( utl::try_from_string(s, i)
&& i != 0);
return true;
}
};

template<typename T>
struct op_from_string<T, decltype(std::declval<T&>().from_string(std::declval<const std::string&>()), void())>
{
inline bool operator()(const std::string& s, T& value) const
{ return value.from_string(s); }
};

template<class T>
template<typename T>
struct op_from_string<T, decltype(T::from_string(std::declval<const std::string&>(), std::declval<T&>()), void())>
{
inline bool operator()(const std::string& s, T& value) const
{ return T::from_string(s, value); }
};

template<class T>
template<typename T>
struct op_from_string<T, typename std::enable_if<std::is_integral<T>::value>::type>
{
inline bool operator()(const std::string& s, T& value) const
@@ -87,7 +204,7 @@ namespace utl
}
};

template<class T>
template<typename T>
struct op_from_string<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{
inline bool operator()(const std::string& s, T& value) const
@@ -98,46 +215,80 @@ namespace utl
return (c != e);
}
};
}

template<class T>
inline void to_string(std::ostream& os, const T& t)
{ __impl::op_to_string<T>()(os, t); }
#ifdef CPPUTILS_HAS_VECTOR
template<typename T>
struct op_from_string<std::vector<T>, void>
{
inline bool operator()(const std::string& str, std::vector<T>& value) const
{
auto* i = str.c_str();
auto* s = i;
auto* e = str.c_str() + str.size();
std::vector<T> tmp;
while (i <= e)
{
if ( s != e
&& ( *i == ','
|| *i == '\0'))
{
tmp.emplace_back();
if (!utl::try_from_string(std::string(s, static_cast<size_t>(i - s)), tmp.back()))
return false;
s = i + 1;
}
++i;
}
value = std::move(tmp);
return true;
}
};
#endif

template<class T>
inline std::string to_string(const T& t)
{
std::ostringstream ss;
to_string(ss, t);
return ss.str();
#ifdef CPPUTILS_HAS_LIST
template<typename T>
struct op_from_string<std::list<T>, void>
{
inline bool operator()(const std::string& str, std::list<T>& value) const
{
auto* i = str.c_str();
auto* s = i;
auto* e = str.c_str() + str.size();
std::list<T> tmp;
while (i <= e)
{
if ( s != e
&& ( *i == ','
|| *i == '\0'))
{
tmp.emplace_back();
if (!utl::try_from_string(std::string(s, static_cast<size_t>(i - s)), tmp.back()))
return false;
s = i + 1;
}
++i;
}
value = std::move(tmp);
return true;
}
};
#endif
}

template<>
inline std::string to_string<std::string>(const std::string& s)
{ return s; }


}

template<class T>
inline bool try_from_string(const std::string& s, T& value)
{ return __impl::op_from_string<T>()(s, value); }

template<class T>
inline T from_string(const std::string& s, T defaultValue)
{
T tmp;
return try_from_string<T>(s, tmp)
? tmp
: defaultValue;
}
namespace std
{

template<class T>
inline T from_string(const std::string& s)
template<typename T_char, typename T_traits, typename X>
inline auto operator<<(basic_ostream<T_char, T_traits>& os, X&& x)
-> decltype(
std::forward<X>(x).to_string(std::declval<basic_ostream<T_char, T_traits>&>()),
std::declval<basic_ostream<T_char, T_traits>&>())
{
T tmp;
if (!try_from_string<T>(s, tmp))
throw exception(std::string("unable to convert string to ") + type_helper<T>::name() + ": " + s);
return tmp;
std::forward<X>(x).to_string(os);
return os;
}

}
}

+ 31
- 1
test/misc/string.cpp View File

@@ -1,3 +1,5 @@
#include <list>
#include <vector>
#include <gtest/gtest.h>
#include <cpputils/misc/string.h>

@@ -56,4 +58,32 @@ TEST(StringHelperTests, from_string)
EXPECT_FALSE(utl::try_from_string("abc", t2));
EXPECT_TRUE (utl::try_from_string("12", t2));
EXPECT_EQ (12, t2.i);
}
}

TEST(StringHelperTests, vector_to_string)
{
EXPECT_EQ(
"1,2,3,4,5",
utl::to_string(std::vector<int> { 1, 2, 3, 4, 5 }));
}

TEST(StringHelperTests, list_to_string)
{
EXPECT_EQ(
"1,2,3,4,5",
utl::to_string(std::list<int> { 1, 2, 3, 4, 5 }));
}

TEST(StringHelperTests, string_to_vector)
{
EXPECT_EQ(
(std::vector<int> { 1, 245, 3, 4, 5 }),
utl::from_string<std::vector<int>>("1, 245, 3, 4, 5"));
}

TEST(StringHelperTests, string_to_list)
{
EXPECT_EQ(
(std::list<int> { 123, 2, 3, 4, 5 }),
utl::from_string<std::list<int>>("123, 2, 3, 4, 5"));
}

Loading…
Cancel
Save