Print comma separated list from std::vector

One way of solving this I have seen is:

std::string separator;
for (auto x : vec) {
  ss << separator << x.specific_detail;
  separator = ",";
}

A fairly easy and reusable way:

#include <vector>
#include <iostream>

template<class Stream, class T, class A>
Stream& printem(Stream&os, std::vector<T, A> const& v)
{
    auto emit = [&os, need_comma = false](T const& x) mutable
    {
        if (need_comma) os << ", ";
        os << x;
        need_comma = true;
    };

    for(T const& x : v) emit(x);
    return os;
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    printem(std::cout, v) << std::endl;
}

And another way which defines an extendable protocol for printing containers:

#include <vector>
#include <iostream>

template<class Container>
struct container_printer;

// specialise for a class of container
template<class T, class A>
struct container_printer<std::vector<T, A>>
{
    using container_type = std::vector<T, A>;

    container_printer(container_type const& c) : c(c) {}

    std::ostream& operator()(std::ostream& os) const 
    {
        const char* sep = "";
        for (const T& x : c) {
            os << sep << x;
            sep = ", ";
        }
        return os;
    }

    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }

    container_type c;
};

template<class Container>
auto print_container(Container&& c)
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type>(c);
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    std::cout << print_container(v) << std::endl;
}

...of course we can go further...

#include <vector>
#include <iostream>

template<class...Stuff>
struct container_printer;

// specialise for a class of container
template<class T, class A, class Separator, class Gap, class Prefix, class Postfix>
struct container_printer<std::vector<T, A>, Separator, Gap, Prefix, Postfix>
{
    using container_type = std::vector<T, A>;

    container_printer(container_type const& c, Separator sep, Gap gap, Prefix prefix, Postfix postfix) 
    : c(c)
    , separator(sep)
    , gap(gap)
    , prefix(prefix)
    , postfix(postfix) {}

    std::ostream& operator()(std::ostream& os) const 
    {
        Separator sep = gap;
        os << prefix;
        for (const T& x : c) {
            os << sep << x;
            sep = separator;
        }
        return os << gap << postfix; 
    }

    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }

    container_type c;
    Separator separator;
    Gap gap;
    Prefix prefix;
    Postfix postfix;
};

template<class Container, class Sep = char, class Gap = Sep, class Prefix = char, class Postfix = char>
auto print_container(Container&& c, Sep sep = ',', Gap gap = ' ', Prefix prefix = '[', Postfix postfix = ']')
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type, Sep, Gap, Prefix, Postfix>(c, sep, gap, prefix, postfix);
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    // json-style
    std::cout << print_container(v) << std::endl;

    // custom
    std::cout << print_container(v, " : ", " ", "(", ")") << std::endl;

    // custom
    std::cout << print_container(v, "-", "", ">>>", "<<<") << std::endl;

}

expected output:

[ 1,2,3,4,5 ]
( 1 : 2 : 3 : 4 : 5 )
>>>1-2-3-4-5<<<

Here is a tiny simple range library:

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
  std::size_t size() const { return std::distance( begin(), end() ); }
  range_t without_front( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {std::next(b, n), e};
  }
  range_t without_back( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {b, std::prev(e, n)};
  }
  range_t only_front( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {b, std::next(b, n)};
  }
  range_t only_back( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {std::prev(end(), n), end()};
  }
};
template<class It>
range_t<It> range(It s, It f) { return {s,f}; }
template<class C>
auto range(C&& c) {
  using std::begin; using std::end;
  return range( begin(c), end(c) );
}

now we are ready.

auto r = range(vec);
for (auto& front: r.only_front()) {
  std::cout << front.x;
}
for (auto& rest: r.without_front()) {
  std::cout << "," << rest.x;
}

Live example.

Now you can get fancier. boost transform iterators, together with boost range, let you do something similar to a list comprehension in python. Or Rangesv3 library for C++2a.

Writing a transform input iterator isn't amazingly hard, it is just a bunch of boilerplate. Simply look at the axioms of input iterator, write a type that stores an arbitrary iterator and forwards most methods to it.

It also stores some function. On * and ->, call the function on the dereferenced iterator.

template<class It, class F>
struct transform_iterator_t {
  using reference=std::result_of_t<F const&(typename std::iterator_traits<It>::reference)>;
  using value_type=reference;
  using difference_type=std::ptrdiff_t;
  using pointer=value_type*;
  using iterator_category=std::input_iterator_tag;

  using self=transform_iterator_t;
  It it;
  F f;
  friend bool operator!=( self const& lhs, self const& rhs ) {
    return lhs.it != rhs.it;
  }
  friend bool operator==( self const& lhs, self const& rhs ) {
    return !(lhs!=rhs);
  }
  self& operator++() {
    ++it;
    return *this;
  }
  self operator++(int) {
    auto r = *this;
    ++*this;
    return r;
  }
  reference operator*() const {
    return f(*it);
  }
  pointer operator->() const {
    // dangerous
    return std::addressof( **this );
  }
};

template<class F>
auto iterator_transformer( F&& f ) {
  return [f=std::forward<F>(f)](auto it){
    return transform_iterator_t<decltype(it), std::decay_t<decltype(f)>>{
      std::move(it), f
    };
  };
}

template<class F>
auto range_transfromer( F&& f ) {
  auto t = iterator_transformer(std::forward<F>(f));
  return [t=std::move(t)](auto&&...args){
    auto tmp = range( decltype(args)(args)... );
    return range( t(tmp.begin()), t(tmp.end()) );
  };
}

Live example of transformer.

And if we add -- we can even use ostream iterator.

Note that std::prev requires a bidirectional iterator, which requires forward iterator concept, which requires that the transform iterator return an actual reference, which is a pain.