#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#include <deque>
#include <algorithm>
#include <string>
// This works similar to ostream_iterator, but doesn't print a delimiter after the final item
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void> {
public:
typedef TChar char_type;
typedef TCharTraits traits_type;
typedef std::basic_ostream<TChar, TCharTraits> ostream_type;
pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL)
: _stream(&stream), _delim(delim), _insertDelim(false)
{ }
pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value) {
if( _delim != NULL ) {
// Don't insert a delimiter if this is the first time the function is called
if( _insertDelim )
(*_stream) << _delim;
else
_insertDelim = true;
}
(*_stream) << value;
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator*() {
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator++() {
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int) {
return *this;
}
private:
ostream_type *_stream;
const char_type *_delim;
bool _insertDelim;
};
#if _MSC_VER >= 1400
// Declare pretty_ostream_iterator as checked
template<typename T, typename TChar, typename TCharTraits>
struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> >
: public std::tr1::true_type
{ };
#endif // _MSC_VER >= 1400
//////////////////////////////////////////////////////////////////////////
//////////////////////Editor's comment: this results in undefined behavior
//////////////////////////////////////////////////////////////////////////
namespace std {
// Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
// These aren't necessary if you do actually include the headers.
template<typename T, typename TAllocator> class vector;
template<typename T, typename TAllocator> class list;
template<typename T, typename TTraits, typename TAllocator> class set;
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;
}
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template<typename T> struct is_container : public std::false_type { };
// Mark vector as a container
template<typename T, typename TAllocator>
struct is_container<std::vector<T, TAllocator> > : public std::true_type { };
// Mark list as a container
template<typename T, typename TAllocator>
struct is_container<std::list<T, TAllocator> > : public std::true_type { };
// Mark set as a container
template<typename T, typename TTraits, typename TAllocator>
struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };
// Mark map as a container
template<typename TKey, typename TValue, typename TTraits, typename TAllocator>
struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };
// Mark deque as a container
template<typename T, typename TAllocator>
struct is_container<std::deque<T, TAllocator>> : public std::true_type { };
//////////////////////////////////////////////////////////////////////////
//////////////////////Editor's comment: My changes begin here
//////////////////////////////////////////////////////////////////////////
template<typename TChar = char, typename TCharTraits = std::char_traits<TChar>>
struct delimiters_v2 {
typedef std::basic_string<TChar, TCharTraits> delimiter_type;
delimiters_v2(const delimiter_type& prefix_, const delimiter_type& delimiter_, const delimiter_type& postfix_)
: prefix(prefix_), delimiter(delimiter_), postfix(postfix_)
{ }
//Since we're passing this around as a value type, trusting the user to provide static strings isn't safe
std::basic_string<TChar, TCharTraits> prefix;
std::basic_string<TChar, TCharTraits> delimiter;
std::basic_string<TChar, TCharTraits> postfix;
};
//This is the class users are meant to specialize in order to get special behavior for a given type [example below]
template<typename Container, typename TChar = char, typename TCharTraits = std::char_traits<TChar>>
struct default_delimiters_v2 {
static delimiters_v2<TChar, TCharTraits> get() {
return delimiters_v2<TChar, TCharTraits>("(", ", ", ")");
}
};
template<typename Container>
struct default_delimiters_v2<Container, wchar_t> {
static delimiters_v2<wchar_t> get() {
return delimiters_v2<wchar_t>(L"(", L", ", L")");
}
};
//Just a copy of the original helper class, with the delimiter parameter removed.
//Instead, the constructor takes a delimiter argument that defaults to default_delimiters_v2<...>::get()
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
struct print_container_helper_v2 {
typedef TChar char_type;
typedef delimiters_v2<TChar, TCharTraits> delimiter_type;
typedef std::basic_ostream<TChar, TCharTraits>& ostream_type;
print_container_helper_v2(const T& container, const delimiter_type& delims = default_delimiters_v2<T, TChar>::get())
: container_(container), delims_(delims)
{ }
void operator()(ostream_type& stream) const {
//I removed the checks against null here, because I'm pretty sure the `basic_string` overload takes care of that case
stream << delims_.prefix;
std::copy(container_.begin(), container_.end(),
pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delims_.delimiter.c_str()));
stream << delims_.postfix;
}
private:
const T& container_;
delimiter_type delims_;
};
//////////////////////////////////////////////////////////////////////////
//////////////////////Editor's comment: These are just a few helper functions to allow template parameter deduction
//////////////////////////////////////////////////////////////////////////
template<typename T, typename TChar, typename TCharTraits>
print_container_helper_v2<T, TChar, TCharTraits>
print_container_function_v2(const T& c,
const delimiters_v2<TChar, TCharTraits>& d)
{
return print_container_helper_v2<T, TChar, TCharTraits>(c, d);
}
template<typename T>
print_container_helper_v2<T> print_container_function_v2(const T& c) {
return print_container_function_v2(c, default_delimiters_v2<T>::get());
}
//////////////////////////////////////////////////////////////////////////
//////////////////////Editor's comment: An example of how to customize the behavior for a specific type
//////////////////////////////////////////////////////////////////////////
template<typename T, typename TAlloc>
struct default_delimiters_v2<std::deque<T, TAlloc>> {
static delimiters_v2<> get() {
return delimiters_v2<>("<", " ", ">");
}
};
template<typename T, typename TChar, typename TCharTraits>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream,
const print_container_helper_v2<T, TChar> &helper) {
helper(stream);
return stream;
}
template<typename T, typename TChar, typename TCharTraits>
typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type
operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container) {
stream << print_container_helper_v2<T, TChar, TCharTraits>(container);
return stream;
}
// Used by the sample below to generate some values
struct fibonacci {
fibonacci() : f1(0), f2(1) { }
int operator()() {
int r = f1 + f2;
f1 = f2;
f2 = r;
return f1;
}
private:
int f1;
int f2;
};
int main() {
std::vector<int> v;
std::generate_n(std::back_inserter(v), 10, fibonacci());
std::cout << v << std::endl;
std::deque<int> d;
std::generate_n(std::back_inserter(d), 10, fibonacci());
std::cout << d << std::endl;
std::cout << "Using helper struct: " << std::endl;
std::cout << print_container_helper_v2<std::deque<int>>(d) << std::endl;
std::cout << print_container_helper_v2<std::deque<int>>(d, delimiters_v2<>("?", "-", "&")) << std::endl;
std::cout << "Using Helper Functions" << std::endl;
std::cout << print_container_function_v2(v) << std::endl;;
std::cout << print_container_function_v2(v, delimiters_v2<>("^", "_", "^")) << std::endl;
std::cout << std::endl;
}