Program Listing for File floats.hpp

Return to documentation for file (/tmp/ws/src/ecl_core/ecl_formatters/include/ecl/formatters/floats.hpp)

/*****************************************************************************
** Ifdefs
*****************************************************************************/

#ifndef ECL_FORMATTERS_FLOATS_HPP_
#define ECL_FORMATTERS_FLOATS_HPP_

/*****************************************************************************
** Includes
*****************************************************************************/

#include <cstdio>   // snprintf
#include <cstring>  // strlen
#include "common.hpp"
#include <ecl/exceptions/standard_exception.hpp>

#if _MSC_VER
#define snprintf _snprintf
#endif

/*****************************************************************************
** Namespaces
*****************************************************************************/

namespace ecl {

/*****************************************************************************
** Format Tags
*****************************************************************************/
enum FloatBase
{
    Fixed,
    Sci,
};

namespace interfaces {

/*****************************************************************************
** FormatFloat
*****************************************************************************/

template <typename Number>
class FormatFloat {
public:
    /******************************************
    ** C&D's
    *******************************************/
    FormatFloat(const int w = -1, const unsigned int p = 4, const ecl::Alignment a = NoAlign, const ecl::FloatBase b = Fixed) :
        prm_width(w),
        prm_precision(p),
        prm_alignment(a),
        prm_base(b),
        width_(&prm_width),
        precision_(&prm_precision),
        alignment_(&prm_alignment),
        base_(&prm_base),
        ready_to_format(false) {}

    virtual ~FormatFloat() {}

    /******************************************
    ** Set Format Parameters
    *******************************************/
    FormatFloat<Number>& precision(const unsigned int p) { *precision_ = p; return *this; }
    FormatFloat<Number>& width(const int w);
    FormatFloat<Number>& align(const ecl::Alignment a);
    FormatFloat<Number>& base(const ecl::FloatBase b);

    /******************************************
    ** Get Format Parameters
    *******************************************/
    int precision() { return *precision_; }

    int width() { return *width_; }

    /******************************************
     * Set Format Combinations
     ******************************************/
    FormatFloat<Number>& operator()(unsigned int p, const int w);
    FormatFloat<Number>& operator()(const unsigned int p, const int w, const ecl::Alignment align, const ecl::FloatBase b);

    /******************************************
    ** Format a value
    *******************************************/
    FormatFloat<Number>& operator() (const Number n);

    /******************************************
    ** Format for streaming
    *******************************************/
    FormatFloat<Number>& operator() (const Number n, const unsigned int p, const int w);
    FormatFloat<Number>& operator() (const Number n, const unsigned int p, const int w, const ecl::Alignment align, const ecl::FloatBase b);

    /******************************************
    ** Insert the formatter into a stream
    *******************************************/
template <typename OutputStream, typename N>
friend OutputStream& operator << (OutputStream& ostream, FormatFloat<N>& formatter);

protected:
    /******************************************
    ** Parameters
    *******************************************/
    int prm_width,tmp_width;
    int prm_precision,tmp_precision;
    ecl::Alignment prm_alignment,tmp_alignment{};
    ecl::FloatBase prm_base,tmp_base;
    int *width_, *precision_;
    ecl::Alignment *alignment_;
    ecl::FloatBase *base_;
    bool ready_to_format;
    Number value_;

    /******************************************
    ** Padding
    *******************************************/
    template <typename OutputStream> void pad(int n, OutputStream &ostream) const;
    template <typename OutputStream> void prePad(int n, OutputStream &ostream) const;
    template <typename OutputStream> void postPad(int n, OutputStream &ostream) const;

    /******************************************
    ** Formatter Functions
    *******************************************/
    template <typename OutputStream> void formatFixed(OutputStream &ostream) const;
    template <typename OutputStream> void formatSci(OutputStream &ostream) const;
};

/*****************************************************************************
* FormatFloat Implementation  [single parameter configuration]
*****************************************************************************/
template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::base(const ecl::FloatBase b)
{
    *base_ = b;
    return *this;
}
template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::width(const int w)
{
    *width_ = w;
    if ( ( *width_ > 0 ) && ( *alignment_ == NoAlign ) )
    {
        *alignment_ = RightAlign;
    }
    return *this;
}

template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::align(const ecl::Alignment a)
{
    *alignment_ = a;
    if ( *alignment_ == NoAlign )
    {
        *width_ = 0;
    }
    return *this;
}

/*****************************************************************************
* FormatFloat Implementation [format combination configuration]
*****************************************************************************/

template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::operator () (const unsigned int p, const int w)
{
    width(w);
    precision(p);
    return *this;
}

template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::operator () (const unsigned int p, const int w, const ecl::Alignment a, const ecl::FloatBase b)
{
    width(w);
    precision(p);
    align(a);
    base(b);
    return *this;
}

/*****************************************************************************
* FormatFloat Implementation [value]
*****************************************************************************/
template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::operator()(const Number n)
{
    value_ = n;
    ready_to_format = true;
    return *this;
}
/*****************************************************************************
* FormatFloat Implementation [temporary formatting]
*****************************************************************************/
template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::operator() (const Number n, unsigned int p, const int w)
{
    width_ = &tmp_width;
    width(w);

    precision_ = &tmp_precision;
    precision(p);

    alignment_ = &tmp_alignment;
    align(prm_alignment);

    base_ = &tmp_base;
    base(prm_base);

    value_ = n;
    ready_to_format = true;
    return *this;
}

template <typename Number>
FormatFloat<Number>& FormatFloat<Number>::operator() (const Number n, const unsigned int p, const int w, const ecl::Alignment a, const ecl::FloatBase b)
{
    width_ = &tmp_width;
    width(w);

    precision_ = &tmp_precision;
    precision(p);

    alignment_ = &tmp_alignment;
    align(a);

    base_ = &tmp_base;
    base(b);

    value_ = n;
    ready_to_format = true;
    return *this;
}


/*****************************************************************************
* FormatFloat Implementation [internal formatting]
*****************************************************************************/
template <typename Number>
  template <typename OutputStream>
void FormatFloat<Number>::formatFixed(OutputStream &ostream) const
{
    static char format_specifier[6] = "%.xff";
    if ( *precision_ < 10 ) {
        format_specifier[2] = '0'+*precision_;
        format_specifier[3] = 'f';
        format_specifier[4] = '\0';
    } else if ( *precision_ < 20 )  {
        format_specifier[2] = '1';
        format_specifier[3] = '0'+(*precision_ - 10);
        format_specifier[4] = 'f';
    } else {
        format_specifier[2] = '2';
        format_specifier[3] = '0';
        format_specifier[4] = 'f';
    }
    static const int buffer_size = 30;
    static char buffer[buffer_size];

    snprintf(buffer, buffer_size, format_specifier, value_);

    /******************************************
    ** Streaming out
    *******************************************/
    int length = strlen(buffer);
    prePad(*width_ - length,ostream);
    ostream << buffer;
    postPad(*width_ - length,ostream);
}

template <typename Number>
  template <typename OutputStream>
void FormatFloat<Number>::formatSci(OutputStream &ostream) const
{
    ostream << "Scientific format is not yet supported." << "\n";
    ostream.flush();
}

template <typename Number>
  template <typename OutputStream>
void FormatFloat<Number>::prePad(int n, OutputStream &ostream) const
{
    if ( n <= 0 ) { return; }
    switch ( *alignment_ )
    {
        case ( NoAlign ) : { break; }
        case ( LeftAlign ) : { break; }
        case ( RightAlign ) : { pad(n,ostream); break; }
        case ( CentreAlign ) : { pad(n/2+ n%2,ostream); break; } // Add the remainder
        default : break;
    }
}

template <typename Number>
  template <typename OutputStream>
void FormatFloat<Number>::postPad(int n, OutputStream &ostream) const
{
    if ( n <= 0 ) { return; }
    switch ( *alignment_ )
    {
        case ( NoAlign ) : { break; }
        case ( LeftAlign ) : { pad(n,ostream); break; }
        case ( RightAlign ) : { break; }
        case ( CentreAlign ) : { pad(n/2,ostream); break; }  // Do not add the remainder
        default : break;
    }
}

template <typename Number>
  template <typename OutputStream>
void FormatFloat<Number>::pad(int n, OutputStream &ostream) const
{
    for (int i = n; i > 0; --i )
    {
        ostream << ' ';
    }
}

/*****************************************************************************
* FormatFloat Implementation [streaming]
*****************************************************************************/
template <typename OutputStream, typename N>
OutputStream& operator << (OutputStream &ostream, FormatFloat<N>& formatter )
{
    bool ready = formatter.ready_to_format;

    ecl_assert_throw(ready, StandardException(LOC,UsageError,"The formatter cannot print any data - "
            "either there is no data available, or you have tried to use the "
            "formatter more than once in a single streaming operation. "
            "C++ produces unspecified results when functors are used multiply "
            "in the same stream sequence, so this is not permitted here.") );

    if ( ready )
    {
        switch(*(formatter.base_) )
        {
            case(Fixed) : {
                formatter.formatFixed(ostream);
                break;
            }
            case(Sci) : {
                formatter.formatSci(ostream);
                break;
            }
        }

        // Switch pointers back to prms if not already there.
        if ( formatter.width_ != &(formatter.prm_width) ) {
            formatter.width_ = &(formatter.prm_width);
            formatter.precision_ = &(formatter.prm_precision);
            formatter.alignment_ = &(formatter.prm_alignment);
            formatter.base_ = &(formatter.prm_base);
        }
        formatter.ready_to_format = false;
    }
    return ostream;
}

} // namespace interfaces

/*****************************************************************************
* Format Classes
*****************************************************************************/
template <>
class Format<float> : public interfaces::FormatFloat<float>
{
    public:
        Format(int w = -1, int p = 4, Alignment a = NoAlign, FloatBase b = Fixed) :
            interfaces::FormatFloat<float>(w, p, a, b) {}

        virtual ~Format() {}
};
template <>
class Format<double> : public interfaces::FormatFloat<double>
{
    public:
        Format(int w = -1, int p = 4, Alignment a = NoAlign, FloatBase b = Fixed) :
            interfaces::FormatFloat<double>(w, p, a, b) {
            }

        virtual ~Format() {}
};

} // namespace ecl

#endif /*ECL_FORMATTERS_FLOATS_HPP_*/