xostream.h
// xostream.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2008
#pragma once
#ifndef Ceda_cxUtils_xostream_H
#define Ceda_cxUtils_xostream_H
#include "cxUtils.h"
#include "xchar.h"
#include "BasicTypeSizes.h"
#include "Detect.h"
#include <string>
#include <string.h>
namespace ceda
{
struct IOutputStream;
class IndentingOutputStreamToOStream;
typedef int64 streamsize;
///////////////////////////////////////////////////////////////////////////////////////////////////
// xostream
/*
An xostream writes text encoded in UTF-8. Code units are of type xchar.
*/
class cxUtils_API xostream
{
cxNotCloneable(xostream)
public:
xostream(IOutputStream& outs);
~xostream();
void close();
void flush();
void endl();
void ends();
/////////////////////////////////////// unformatted output /////////////////////////////////////
// Write the given string directly to the output stream, without accounting for field width
// fill character and justification (contrast with put(str,numChars) method)
void write(const xchar* str, ssize_t numChars);
// Write the given bytes to the underlying output stream
void writebytes(const uint8* buffer, ssize_t numBytes);
/////////////////////////////////////// formatted output /////////////////////////////////////
/*
The put functions are used for formatted output - meaning that
items are written to the output stream accounting for the current field width, fill character
and justification.
Note: it appears that in STL 'put' is actually used for unformatted output, so some renaming is
in order.
VS2008
std::basic_ostream<char> writes an unsigned char as a character not an integer.
std::basic_ostream<wchar_t> writes an unsigned char as an integer not a character.
This behaviour seems unnecessarily inconsistent.
*/
void put(bool);
void put(char);
void put(signed char);
void put(unsigned char);
void put(wchar_t);
void put(char16_t);
void put(char32_t);
void put(short);
void put(unsigned short);
void put(int);
void put(unsigned int);
void put(long);
void put(unsigned long);
void put(long long);
void put(unsigned long long);
void put(float);
void put(double);
void put(long double);
void put(const void* f);
void put(const char8* str, ssize_t numChars);
inline void put(ConstString8Z str) { put(str, strlen(str)); }
inline void put(const std::string& s) { put(s.data(), s.size()); }
void put_internal(const xchar* buffer, ssize_t ln, ssize_t tn);
typedef xostream& (*Manipulator)(xostream& os);
xostream& operator<<(Manipulator f)
{
return f(*this);
}
streamsize precision() const;
streamsize precision(streamsize p);
streamsize width() const;
streamsize width(streamsize w);
void Indent(ssize_t offset);
ssize_t GetIndent() const;
IndentingOutputStreamToOStream* m_os;
};
inline xostream& operator<<(xostream& os, bool f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, char f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, signed char f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, unsigned char f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, wchar_t f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, char16_t f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, char32_t f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, short f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, unsigned short f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, int f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, unsigned int f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, long f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, unsigned long f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, long long f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, unsigned long long f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, float f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, double f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, long double f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, const void* f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, ConstString8Z f) { os.put(f); return os; }
inline xostream& operator<<(xostream& os, const std::string& s) { os.put(s); return os; }
template <typename T> using xostream_insertion_t = decltype(std::declval<xostream&>() << std::declval<T const &>());
template <typename T> using has_xostream_insertion = is_detected<xostream_insertion_t, T>;
// If a class has a method with signature
// void Write(xostream&) const
// then automatically allow operator<< to be used.
template <typename T> using Write_xostream_method_t = decltype(std::declval<const T&>().Write(std::declval<xostream&>()));
template <typename T> using has_Write_xostream_method = is_detected<Write_xostream_method_t, T>;
template <typename T, std::enable_if_t<has_Write_xostream_method<T>::value, int> = 0>
inline xostream& operator<<(xostream& os, const T& x)
{
x.Write(os);
return os;
}
// store function pointer and argument value
template<typename Arg>
struct SingleArgManip
{
SingleArgManip(void (*fn)(xostream&, Arg), Arg arg)
: m_fn(fn), m_arg(arg)
{
}
void (*m_fn)(xostream&, Arg); // the function pointer
Arg m_arg; // the argument value
};
// insert by calling function with output stream and argument
template<typename Arg>
inline xostream& operator<<(xostream& os, const SingleArgManip<Arg>& m)
{
(*m.m_fn)(os, m.m_arg);
return os;
}
typedef int ios_flags;
cxUtils_API SingleArgManip<ssize_t> setprecision(ssize_t p);
cxUtils_API SingleArgManip<ssize_t> setw(ssize_t w);
cxUtils_API SingleArgManip<xchar> setfill(xchar c);
cxUtils_API SingleArgManip<ios_flags> setiosflags(ios_flags);
cxUtils_API SingleArgManip<ios_flags> resetiosflags(ios_flags);
cxUtils_API SingleArgManip<int> setbase(int);
inline xostream& endl(xostream& os)
{
os.endl();
return os;
}
inline xostream& ends(xostream& os)
{
os.ends();
return os;
}
inline xostream& flush(xostream& os)
{
os.flush();
return os;
}
#define mDeclareManipulator(x) cxUtils_API xostream& x(xostream& os);
mDeclareManipulator(boolalpha)
mDeclareManipulator(dec)
mDeclareManipulator(fixed)
mDeclareManipulator(hex)
mDeclareManipulator(internal)
mDeclareManipulator(left)
mDeclareManipulator(noboolalpha)
mDeclareManipulator(noshowbase)
mDeclareManipulator(noshowpoint)
mDeclareManipulator(noshowpos)
mDeclareManipulator(noskipws)
mDeclareManipulator(nounitbuf)
mDeclareManipulator(nouppercase)
mDeclareManipulator(oct)
mDeclareManipulator(right)
mDeclareManipulator(scientific)
mDeclareManipulator(showbase)
mDeclareManipulator(showpoint)
mDeclareManipulator(showpos)
mDeclareManipulator(skipws)
mDeclareManipulator(unitbuf)
mDeclareManipulator(uppercase)
mDeclareManipulator(fixedorscientific)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Indenter
// Used to apply an indent in a lexical scope to the given xostream
class cxUtils_API Indenter
{
cxNotCloneable(Indenter)
public:
Indenter(xostream& os, ssize_t indent = 4);
~Indenter();
private:
xostream& m_os;
ssize_t m_indent;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Write n characters starting at position str to stdout.
cxUtils_API void WriteToStdout(const xchar* str, ssize_t n);
extern cxUtils_API xostream xcout;
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
When printing randomly generated char values in tests it is all too easy to produce strings which
are not valid UTF-8, and this can cause StringConversionException exceptions to be thrown
(see xstring.h).
Rather than use
os << x
you can use
os << CharToInt(x)
to deal with the case where x is of type char, and you want it to be printed as an int not a char
because it might not be in the range 0x00 to 0x7f.
*/
template <typename T>
struct MapCharToInt { using type = T; };
template <>
struct MapCharToInt<char> { using type = int; };
template <typename T>
auto CharToInt(const T& x)
{
return static_cast<typename MapCharToInt<T>::type>(x);
}
} // namespace ceda
#endif // include guard