patterncppMinor
String concatenation utility in C++11
Viewed 0 times
concatenationstringutility
Problem
I published a concatenation utility lastly and I feel it could be very good help for many people, but before I promote it, I would like to polish the details as much as I can. So I'll like to ask you to review this less than 300 lines of code.
```
#ifndef THEYPSILON_CONCAT
#define THEYPSILON_CONCAT
#include
#include
namespace theypsilon {
template
struct separator_t {
const CharT* sep;
constexpr explicit separator_t(const CharT* s) noexcept: sep{s} {}
};
template
constexpr separator_t separator(const CharT* s) {
return separator_t(s);
}
namespace sep {
constexpr char none [] = "";
constexpr char space[] = " ";
constexpr char endl [] = "\n";
constexpr char coma [] = ", ";
constexpr char plus [] = " + ";
};
namespace { // type helpers and traits
template
struct has_const_iterator {
private:
typedef char yes;
typedef struct { char array[2]; } no;
template static yes test(typename C::const_iterator*);
template static no test(...);
public:
static const bool value = sizeof(test(0)) == sizeof(yes);
typedef T type;
};
template
struct has_begin_end {
template static char (&f(typename std::enable_if(&C::begin)),
typename C::const_iterator(C::)() const>::value, void>::type))[1];
template static char (&f(...))[2];
template static char (&g(typename std::enable_if(&C::end)),
typename C::const_iterator(C::)() const>::value, void>::type))[1];
template static char (&g(...))[2];
static bool const beg_value = sizeof(f(0)) == 1;
static bool const end_value = sizeof(g(0)) == 1;
};
template
constexpr bool is_writable_stream() {
return std::is_same>::value ||
std::is_s
```
#ifndef THEYPSILON_CONCAT
#define THEYPSILON_CONCAT
#include
#include
namespace theypsilon {
template
struct separator_t {
const CharT* sep;
constexpr explicit separator_t(const CharT* s) noexcept: sep{s} {}
};
template
constexpr separator_t separator(const CharT* s) {
return separator_t(s);
}
namespace sep {
constexpr char none [] = "";
constexpr char space[] = " ";
constexpr char endl [] = "\n";
constexpr char coma [] = ", ";
constexpr char plus [] = " + ";
};
namespace { // type helpers and traits
template
struct has_const_iterator {
private:
typedef char yes;
typedef struct { char array[2]; } no;
template static yes test(typename C::const_iterator*);
template static no test(...);
public:
static const bool value = sizeof(test(0)) == sizeof(yes);
typedef T type;
};
template
struct has_begin_end {
template static char (&f(typename std::enable_if(&C::begin)),
typename C::const_iterator(C::)() const>::value, void>::type))[1];
template static char (&f(...))[2];
template static char (&g(typename std::enable_if(&C::end)),
typename C::const_iterator(C::)() const>::value, void>::type))[1];
template static char (&g(...))[2];
static bool const beg_value = sizeof(f(0)) == 1;
static bool const end_value = sizeof(g(0)) == 1;
};
template
constexpr bool is_writable_stream() {
return std::is_same>::value ||
std::is_s
Solution
The only advice I could offer is to use "modern" style traits that work nicely with the standard library through the usage of
For example,
Now you get the benefit of
It's fairly uncommon to define traits as
On a final note,
You can learn more about expression SFINAE here. As for the reason that there's
std::integral_constant and expression SFINAE.For example,
has_const_iterator can be rewritten like this:template
struct void_ {
using type = void;
};
template
using Void = typename void_::type;
template
struct has_const_iterator : public std::false_type {};
template
struct has_const_iterator> : public std::true_type {};Now you get the benefit of
std::integral_constant without having to redefine ::value. It'll also work with tag dispatching that takes in std::true_type or std::false_type as parameters.It's fairly uncommon to define traits as
constexpr bool functions as well since this disallows the use of higher ordered meta functions, so I'd suggest moving those into their own traits like you did with has_begin_end and has_const_iterator.On a final note,
has_begin_end can be simplified immensely through the use of expression SFINAE as seen here:struct has_begin_end_impl {
template().begin()),
typename E = decltype(std::declval().end())>
static std::true_type test(int);
template
static std::false_type test(...);
};
template
struct has_begin_end : public decltype(has_begin_end_impl::test(0)) {};You can learn more about expression SFINAE here. As for the reason that there's
test(int) and test(...), that's for making the call test(0) unambiguous.Code Snippets
template<typename...>
struct void_ {
using type = void;
};
template<typename... Args>
using Void = typename void_<Args...>::type;
template<typename T, typename U = void>
struct has_const_iterator : public std::false_type {};
template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : public std::true_type {};struct has_begin_end_impl {
template<typename T, typename B = decltype(std::declval<T&>().begin()),
typename E = decltype(std::declval<T&>().end())>
static std::true_type test(int);
template<typename...>
static std::false_type test(...);
};
template<typename T>
struct has_begin_end : public decltype(has_begin_end_impl::test<T>(0)) {};Context
StackExchange Code Review Q#58195, answer score: 7
Revisions (0)
No revisions yet.