ListToOStream.h

// ListToOStream.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2008

#pragma once
#ifndef Ceda_cxUtils_ListToOStream_H
#define Ceda_cxUtils_ListToOStream_H

#include "xostream.h"
#include "Detect.h"
#include <utility>
#include <vector>
#include <deque>
#include <forward_list>
#include <list>
#include <set>
#include <map>
#include <array>

namespace ceda
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// Decorator to display in brackets

template <xchar leftBracket, xchar rightBracket, class Base>
class BracketedMixin : public Base {};

template <xchar leftBracket, xchar rightBracket, class Base> 
xostream& operator<<(xostream& os, const BracketedMixin<leftBracket,rightBracket,Base>& v)
{
    os << leftBracket << (const Base&) v << rightBracket;
    return os;
}

template <xchar leftBracket, xchar rightBracket, class Base> 
const BracketedMixin<leftBracket,rightBracket,Base>& AsBracketed(const Base& b)
{
    return (const BracketedMixin<leftBracket,rightBracket,Base>&) b;
}

template <typename Base> 
const BracketedMixin<'[',']',Base>& AsSquareBracketed(const Base& b)
{
    return (const BracketedMixin<'[',']',Base>&) b;
}

template <typename Base> 
const BracketedMixin<'(',')',Base>& AsRoundBracketed(const Base& b)
{
    return (const BracketedMixin<'(',')',Base>&) b;
}

template <typename Base> 
const BracketedMixin<'{','}',Base>& AsCurlyBracketed(const Base& b)
{
    return (const BracketedMixin<'{','}',Base>&) b;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Decorator to apply an indent

template <ssize_t indent,class Base>
class IndentMixin : public Base {};

template <ssize_t indent, class Base> 
xostream& operator<<(xostream& os, const IndentMixin<indent,Base>& v)
{
    Indenter indenter(os,indent);
    os << (const Base&) v;
    return os;
}

template <ssize_t indent, class Base> 
const IndentMixin<indent,Base>& AsIndented(const Base& b)
{
    return (const IndentMixin<indent,Base>&) b;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

enum EDelimiterPlacement
{
    DELIMIT_NEVER,
    DELIMIT_BEFORE,
    DELIMIT_AFTER,
    DELIMIT_BETWEEN,
    DELIMIT_AROUND,
};

template <xchar delimiter, EDelimiterPlacement placement, class It>
void WriteList(xostream& os, It p1, It p2)
{
    for (It p = p1 ; p != p2 ; ++p)
    {
        if (placement == DELIMIT_BEFORE || 
            placement == DELIMIT_AROUND || 
            (placement == DELIMIT_BETWEEN && p != p1))
        {
            os << delimiter;
        }
        os << *p;

        if (placement == DELIMIT_AFTER)
            os << delimiter;
    }
    if (placement == DELIMIT_AROUND)
        os << delimiter;
}

template <xchar delimiter, EDelimiterPlacement placement,class Base>
struct ListMixin : public Base 
{
};

template <xchar delimiter, EDelimiterPlacement placement,class Base>
xostream& operator<<(xostream& os, const ListMixin<delimiter,placement,Base>& v)
{
    WriteList<delimiter,placement>(os, v.begin(), v.end());
    return os;
}

template <xchar delimiter, EDelimiterPlacement placement,class Base>
const ListMixin<delimiter,placement,Base>& AsList(const Base& b)
{
    return (const ListMixin<delimiter,placement,Base>&) b;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename It> void WriteLineFeedSeparatedList(xostream& os, It p1, It p2)
{
    for (It p = p1 ; p != p2 ; ++p)
    {
        os << *p << '\n';
    }
}

template <typename It> void WriteLineFeedSeparatedPtrList(xostream& os, It p1, It p2)
{
    for (It p = p1 ; p != p2 ; ++p)
    {
        os << **p << '\n';
    }
}

template <typename It> void WriteCommaSeparatedListToStream(xostream& os, It p1, It p2)
{
    for (It p = p1 ; p != p2 ; ++p)
    {
        if (p != p1) os << ',';
        os << *p;
    }
}

template <typename It> void WriteCommaSeparatedPtrListToStream(xostream& os, It p1, It p2)
{
    for (It p = p1 ; p != p2 ; ++p)
    {
        if (p != p1) os << ',';
        os << **p;
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Base>
struct CommaSeparatedListMixin : public Base {};

template <typename Base> 
xostream& operator<<(xostream& os, const CommaSeparatedListMixin<Base>& v)
{
    WriteCommaSeparatedListToStream(os, v.begin(), v.end());
    return os;
}

template <typename Base> 
const CommaSeparatedListMixin<Base>& AsCommaSeparatedList(const Base& b)
{
    return (const CommaSeparatedListMixin<Base>&) b;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

template <typename T, size_t N, std::enable_if_t<has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::array<T,N>& v)
{
    os << AsSquareBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename T, typename A, std::enable_if_t<has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::vector<T,A>& v)
{
    os << AsSquareBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename T,typename A, std::enable_if_t<has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::deque<T,A>& v)
{
    os << AsSquareBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename T,typename A, std::enable_if_t<has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::forward_list<T,A>& v)
{
    os << AsSquareBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename T,typename A, std::enable_if_t<has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::list<T,A>& v)
{
    os << AsSquareBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename U, typename V, std::enable_if_t<has_xostream_insertion<U>::value && has_xostream_insertion<V>::value, int> = 0>
xostream& operator<<(xostream& os, const std::pair<U,V>& p)
{
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}

template <typename K,typename P,typename A, std::enable_if_t<has_xostream_insertion<K>::value, int> = 0>
xostream& operator<<(xostream& os, const std::set<K,P,A>& v)
{
    os << AsCurlyBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename K,typename P,typename A, std::enable_if_t<has_xostream_insertion<K>::value, int> = 0> 
xostream& operator<<(xostream& os, const std::multiset<K,P,A>& v)
{
    os << AsCurlyBracketed(AsCommaSeparatedList(v));
    return os;
}

template <typename K,typename T,typename P,typename A, std::enable_if_t<has_xostream_insertion<K>::value && has_xostream_insertion<T>::value, int> = 0>
xostream& operator<<(xostream& os, const std::map<K,T,P,A>& v)
{
    os << AsCurlyBracketed(AsCommaSeparatedList(v));
    return os;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Base>
struct SquareBracketedListWithLineFeeds : public Base {};

template <typename Base> 
xostream& operator<<(xostream& os, const SquareBracketedListWithLineFeeds<Base>& v)
{
    os << '[' << '\n';
    {
        Indenter indent(os);
        WriteLineFeedSeparatedList(os, v.begin(), v.end());
    }
    os << ']';
    return os;
}

template <typename Base> 
const SquareBracketedListWithLineFeeds<Base>& AsSquareBracketedListWithLineFeeds(const Base& b)
{
    return (const SquareBracketedListWithLineFeeds<Base>&) b;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Base>
struct SquareBracketedPtrListWithLineFeeds : public Base {};

template <typename Base> 
xostream& operator<<(xostream& os, const SquareBracketedPtrListWithLineFeeds<Base>& v)
{
    os << '[' << '\n';
    {
        Indenter indent(os);
        WriteLineFeedSeparatedPtrList(os, v.begin(), v.end());
    }
    os << ']';
    return os;
}

template <typename Base> 
const SquareBracketedPtrListWithLineFeeds<Base>& AsSquareBracketedPtrListWithLineFeeds(const Base& b)
{
    return (const SquareBracketedPtrListWithLineFeeds<Base>&) b;
}

} // namespace ceda

#endif // include guard