/**************************************************************************
 *
 * fmtflags_manip.cpp - Example program demonstrating the implementation
 * of a user-defined manipulator for convenient and exception-safe setting
 * and restoring of stream formatting flags.
 *
 * $Id: fmtflags_manip.cpp 648752 2008-04-16 17:01:56Z faridz $
 *
 ***************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 *
 **************************************************************************/

#include <ios>        // for hex
#include <iomanip>    // for __rw_smanip
#include <iostream>   // for cout

#include <examples.h>


// implementation class of the fmtflags manipulator that saves
// a stream object's fmtflags state before optionally setting
// their new value and restores the saved state when destroyed
// uses ios::basefield as an invalid flags value (it's a valid
// mask but not a valid flags value)
class fmtflags_manip
{
    std::ios_base           *strm_;
    std::ios_base::fmtflags  saved_;

public:
    fmtflags_manip (): strm_ (), saved_ (std::ios_base::basefield) { }

    void operator() (std::ios_base           &strm,
                     std::ios_base::fmtflags  flags) const {

        const_cast<fmtflags_manip*>(this)->strm_  = &strm;
        const_cast<fmtflags_manip*>(this)->saved_ = strm.flags ();

        if (flags != std::ios_base::basefield)
            strm.flags (flags);
    }

    ~fmtflags_manip () {
        if (strm_)
            strm_->flags (saved_);
    }
};


// saveflags manipulator to temporarily set formatting flags and
// restore their initial value at the end of an insertion statement
inline std::__rw_smanip<fmtflags_manip, std::ios_base::fmtflags>
saveflags (std::ios_base::fmtflags flags = std::ios_base::basefield)
{
    typedef std::__rw_smanip<fmtflags_manip, std::ios_base::fmtflags> Manip;

    return Manip (flags);
}


// dummy class whose insertion operator always throws
struct BadClass { };


inline std::ostream&
operator<< (std::ostream &strm, BadClass&)
{
    // inserter that always throws an exception
    return throw "exception", strm;

}


int main ()
{
    try {
        BadClass throw_exception;

        // use the saveflags manipulator to set the formatting flags
        // for the rest of this insertion statement only and have it
        // restore the  previous flags at the end of the statement
        // or on exception
        std::cout << saveflags (std::cout.hex)
                  << std::showbase
                  << "hex 1234: " << 0x1234 << '\n'
                  << "hex 2345: " << 0x2345 << '\n'
                  << throw_exception    // inserting will throw
                  << "never output!";   // this string is never output
    }
    catch (...) {
        // show that default formatting flags (decimal output and no
        // showbase) have been restored during stack unwinding above
        std::cout << "dec 3456:   " << 3456 << '\n'
                  << std::oct
                  << "oct 4567:   " << 04567 << '\n';
    }

    // use ordinary manipulators to set hex output with base prefix
    std::cout << std::hex << std::showbase
              << "hex 5678: " << 0x5678 << '\n'
              << "hex 6789: " << 0x6789 << '\n';

    // the same flags set above are still in effect
    std::cout << "hex 789a: " << 0x789a << '\n';

    return 0;
}
