Nice programing

스트림의 다음 항목을 변경하는 C ++ 사용자 정의 스트림 조작기

nicepro 2021. 1. 7. 21:19
반응형

스트림의 다음 항목을 변경하는 C ++ 사용자 정의 스트림 조작기


C ++에서 16 진수로 숫자를 인쇄하려면 다음을 수행합니다.

int num = 10;
std::cout << std::hex << num; // => 'a'

다음과 같이 스트림에 항목을 추가하는 조작자를 만들 수 있다는 것을 알고 있습니다.

std::ostream& windows_feed(std::ostream& out)
{
    out << "\r\n";
    return out;
}

std::cout << "Hello" << windows_feed; // => "Hello\r\n"

그러나 'hex'와 같이 스트림에 표시되도록 항목을 수정하는 조작기를 어떻게 만들 수 있습니까? 간단한 예로서 여기에 plusone 조작기를 어떻게 만들까요? :

int num2 = 1;
std::cout << "1 + 1 = " << plusone << num2; // => "1 + 1 = 2"

// note that the value stored in num2 does not change, just its display above.
std::cout << num2; // => "1"

먼저 각 스트림에 상태를 저장해야합니다. 다음과 같이 iword전달 하는 함수 와 색인으로이를 수행 할 수 있습니다 xalloc.

inline int geti() { 
    static int i = ios_base::xalloc();
    return i;
}

ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; } 
ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; }

그것을 제자리에두면 모든 스트림에서 이미 일부 상태를 검색 할 수 있습니다. 이제 각 출력 작업에 연결하기 만하면됩니다. 숫자 출력은 잠재적으로 로케일에 따라 다르기 때문에 패싯에 의해 수행됩니다. 그래서 당신은 할 수 있습니다

struct my_num_put : num_put<char> {
    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 

    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 
}; 

이제 테스트 할 수 있습니다.

int main() {
    // outputs: 11121011
    cout.imbue(locale(locale(),new my_num_put));
    cout << add_one << 10 << 11 
         << add_none << 10 << 11;
}

다음 번호 만 증가 시키려면로 0호출 할 때마다 단어를 다시로 설정하십시오 do_put.


나는 이것에 대해 Neil Butterworth에 전적으로 동의하지만, 당신이 사용하는 특정 경우에 당신은 완전히 끔찍한 해킹을 할 수 있습니다. 프로덕션 코드에서는이 작업을 수행하지 마십시오. 많은 버그가 있습니다. 한 가지는 위의 한 줄에서만 작동하며 기본 스트림의 상태를 변경하지 않습니다.

class plusone_stream : public std::ostream
{
  public:
    std::ostream operator<<(int i)
    {
      _out << i+1;
      return *this;
    }
};

std::ostream& plusone(std::ostream& out)
{
    return plusone_stream(out);
}

질문에 대한 직접적인 대답은 아니지만 평범한 오래된 함수를 사용하는 것이 완전한 조작기를 작성하는 것보다 구현하기 쉽고 사용하기 더 쉽다고 생각하지 않습니까?

#include <sstream>

template<typename T>
std::string plusone(T const& t) {
    std::ostringstream oss;
    oss << (t + 1);
    return oss.str();
}

용법:

cout << plusone(42);

"사용하기 쉽다"는 것은 사용자가 "다음 항목에만 영향을 미치는가 아니면 모든 후속 항목에 영향을 미치는가?"라고 스스로 질문 할 필요가 없음을 의미합니다. 검사를 통해 함수의 인수 만 영향을받는 것은 분명합니다.

(예를 plusone()들어, T대신 a 반환하여 더 단순화 할 수 있지만 a 를 반환 std::string하면 일반적인 경우가됩니다.)


.NET을 사용하지 않고 테스트 케이스에 대한 간단한 솔루션을 만들었습니다 <iomanip>. 동일한 접근 방식이 실생활에서도 효과가있을 것이라고 약속 할 수 없습니다.

기본 접근 방식은 cout << plusone임시 보조 개체 ( PlusOnePlus) 반환하는 것 입니다.이 개체 operator <<에는 추가를 수행하는 오버로드 가 있습니다.

Windows에서 테스트했습니다.

PlusOne plusone;
cout << plusone << 41

예상대로 "42"를 생성합니다. 코드는 다음과 같습니다.

class PlusOnePlus {
public:
    PlusOnePlus(ostream& os) : m_os(os) {}
    // NOTE: This implementation relies on the default copy ctor,
    // assignment, etc.
private:
    friend ostream& operator << (PlusOnePlus& p, int n);
    ostream& m_os;
};

class PlusOne {
public:
    static void test(ostream& os);
};

PlusOnePlus operator << (ostream& os, const PlusOne p)
{
    return PlusOnePlus(os);
}

ostream& operator << (PlusOnePlus& p, int n)
{
    return p.m_os << n + 1;
}

void PlusOne::test(ostream& os)
{
    PlusOne plusone;
    os << plusone << 0 << endl;
    os << plusone << 41 << endl;
}

편집 : 내가 PlusOnePlus. 강력한 구현은 아마도 다음을 정의 할 것입니다.


스트림 상태로 플레이해야합니다. 주제에 대한 다음 링크를 북마크에 추가했습니다.

Maciej Sobczak 라이브러리는 더 이상 온라인에서 사용할 수 없으며 라이센스가 허용하는대로 (내가 틀렸다면 수정) 여기에 내가 망각에서 구할 수 있었던 주요 파일의 사본이 있습니다.

// streamstate.h
//
// Copyright (C) Maciej Sobczak, 2002, 2003
//
// Permission to copy, use, modify, sell and distribute this software is
// granted provided this copyright notice appears in all copies.  This software
// is provided "as is" without express or implied warranty, and with no claim
// as to its suitability for any purpose.
//
// <http://lists.boost.org/Archives/boost/2002/10/38275.php>
// <http://www.ddj.com/dept/cpp/184402062?pgno=1>
// <http://www.msobczak.com/prog/publications.html>

#ifndef STREAMSTATE_H_INCLUDED
#define STREAMSTATE_H_INCLUDED

#include <ios>
#include <istream>
#include <ostream>

// helper exception class, thrown when the source of error
// was in one of the functions managing the additional state storage
class StreamStateException : public std::ios_base::failure
{
public:
    explicit StreamStateException()
        : std::ios_base::failure(
            "Error while managing additional IOStream state.")
    {
    }
};

// State should be:
// default-constructible
// copy-constructible
// assignable

// note: the "void *" slot is used for storing the actual value
//       the "long" slot is used to propagate the error flag
template
<
    class State,
    class charT = char,
    class traits = std::char_traits<charT>
>
class streamstate
{
public:
    // construct with the default state value
    streamstate() {}

    // construct with the given stream value
    streamstate(const State &s) : state_(s) {}

    // modifies the stream
    std::basic_ios<charT, traits> &
    modify(std::basic_ios<charT, traits> &ios) const
    {
        long *errslot;
        void *&p = state_slot(ios, errslot);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // here, do-nothing-in-case-of-error semantics
        if (ios.bad())
            return ios;

        if (p == NULL)
        {
            // copy existing state object if this is new slot
            p = new State(state_);
            ios.register_callback(state_callback, 0);
        }
        else
            *static_cast<State*>(p) = state_;

        return ios;
    }

    // gets the current (possibly default) state from the slot
    static State & get(std::basic_ios<charT, traits> &ios)
    {
        long *errslot;
        void *&p = state_slot(ios, errslot);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // this function returns a reference and therefore
        // the only sensible error reporting is via exception
        if (ios.bad())
            throw StreamStateException();

        if (p == NULL)
        {
            // create default state if this is new slot
            p = new State;
            ios.register_callback(state_callback, 0);
        }

        return *static_cast<State*>(p);
    }

private:
    // manages the destruction and format copying
    // (in the latter case performs deep copy of the state)
    static void state_callback(std::ios_base::event e,
        std::ios_base &ios, int)
    {
        long *errslot;
        if (e == std::ios_base::erase_event)
        {
            // safe delete if state_slot fails
            delete static_cast<State*>(state_slot(ios, errslot));
        }
        else if (e == std::ios_base::copyfmt_event)
        {
            void *& p = state_slot(ios, errslot);
            State *old = static_cast<State*>(p);

            // Standard forbids any exceptions from callbacks
            try
            {
                // in-place deep copy
                p = new State(*old);
    }
            catch (...)
            {
                // clean the value slot and
                // set the error flag in the error slot
                p = NULL;
                *errslot = std::ios_base::badbit;
            }
        }
    }

    // returns the references to associated slot
    static void *& state_slot(std::ios_base &ios, long *&errslot)
    {
        static int index = std::ios_base::xalloc();
        void *&p = ios.pword(index);
        errslot = &(ios.iword(index));

        // note: if pword failed,
        // then p is a valid void *& initialized to 0
        // (27.4.2.5/5)

        return p;
    }

    State state_;
};

// partial specialization for iword functionality
template
<
    class charT,
    class traits
>
class streamstate<long, charT, traits>
{
public:
    // construct with the default state value
    streamstate() {}

    // construct with the given stream value
    streamstate(long s) : state_(s) {}

    // modifies the stream
    // the return value is not really useful,
    // it has to be downcasted to the expected stream type
    std::basic_ios<charT, traits> &
    modify(std::basic_ios<charT, traits> &ios) const
    {
        long &s = state_slot(ios);
        s = state_;

        return ios;
    }

    static long & get(std::basic_ios<charT, traits> &ios)
    {
        return state_slot(ios);
    }

private:
    static long & state_slot(std::basic_ios<charT, traits> &ios)
    {
        static int index = std::ios_base::xalloc();
        long &s = ios.iword(index);

        // this function returns a reference and we decide
        // to report errors via exceptions
        if (ios.bad())
            throw StreamStateException();

        return s;
    }

    long state_;
};

// convenience inserter for ostream classes
template
<
    class State,
    class charT,
    class traits
>
std::basic_ostream<charT, traits> &
operator<<(std::basic_ostream<charT, traits> &os,
    const streamstate<State> &s)
{
    s.modify(os);
    return os;
}

// convenience extractor for istream classes
template
<
    class State,
    class charT,
    class traits
>
std::basic_istream<charT, traits> &
operator>>(std::basic_istream<charT, traits> &is,
    const streamstate<State> &s)
{
    s.modify(is);
    return is;
}

// the alternative if there is a need to have
// many different state values of the same type
// here, the instance of streamstate_value encapsulates
// the access information (the slot index)

template
<
    class State,
    class charT = char,
    class traits = std::char_traits<char>
>
class streamstate_value
{
public:

    streamstate_value()
        : index_(-1)
    {
    }

    // returns a reference to current (possibly default) state
    State & get(std::basic_ios<charT, traits> &ios)
    {
        long *errslot;
        void *&p = state_slot(ios, errslot, index_);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // this function returns a reference and the only
        // sensible way of error reporting is via exception
        if (ios.bad())
            throw StreamStateException();

        if (p == NULL)
        {
            // create default state if this is new slot
            p = new State;
            ios.register_callback(state_callback, index_);
        }

        return *static_cast<State*>(p);
    }

private:

    // manages the destruction and format copying
    // (in the latter case performs deep copy of the state)
    static void state_callback(std::ios_base::event e,
        std::ios_base &ios, int index)
    {
        long *errslot;
        if (e == std::ios_base::erase_event)
        {
            // safe delete if state_slot fails
            delete static_cast<State*>(state_slot(ios, errslot, index));
        }
        else if (e == std::ios_base::copyfmt_event)
        {
            void *& p = state_slot(ios, errslot, index);
            State *old = static_cast<State*>(p);

            // Standard forbids any exceptions from callbacks
            try
            {
                // in-place deep copy
                p = new State(*old);
    }
            catch (...)
            {
                // clean the value slot and set the error flag
                // in the error slot
                p = NULL;
                *errslot = std::ios_base::badbit;
            }
        }
    }

    // returns the references to associated slot
    static void *& state_slot(std::ios_base &ios,
        long *& errslot, int & index)
    {
        if (index < 0)
        {
            // first index usage
            index = std::ios_base::xalloc();
        }

        void *&p = ios.pword(index);
        errslot = &(ios.iword(index));

        // note: if pword failed,
        // then p is a valid void *& initialized to 0
        // (27.4.2.5/5)

        return p;
    }

    int index_;
};

// partial specialization for iword functionality
template
<
    class charT,
    class traits
>
class streamstate_value<long, charT, traits>
{
public:
    // construct with the default state value
    streamstate_value()
        : index_(-1)
    {
    }

    long & get(std::basic_ios<charT, traits> &ios)
    {
        if (index_ < 0)
        {
            // first index usage
            index_ = std::ios_base::xalloc();
        }

        long &s = ios.iword(index_);
        if (ios.bad())
            throw StreamStateException();

        return s;
    }

private:
    long index_;
};

#endif // STREAMSTATE_H_INCLUDED 

litb의 접근 방식은 "올바른 방법"이며 복잡한 작업에 필요하지만 이와 같은 방법으로도 충분할 수 있습니다. 취향에 사생활과 우정을 추가하십시오.

struct PlusOne
{
   PlusOne(int i) : i_(i) { }
   int i_;
};

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << (po.i_ + 1);
}

std::cout << "1 + 1 = " << PlusOne(num2); // => "1 + 1 = 2"

In this simple example creating and streaming a temporary object doesn't seem much more helpful than defining a function plusOne() as someone already suggested. But suppose you wanted it to work like this:

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << po.i_ << " + 1 = " << (po.i_ + 1);
}

std::cout << PlusOne(num2); // => "1 + 1 = 2"

The hex, dec and oct manipulators simply change the basefield property of the existing stream.

See C++ Reference for more deatail about these manipulators.

As posted in Neil Butterworth's answer, you would need to extend the existing stream classes, or create your own, in order to have manipulators that affect future values inserted into the stream.

In the example of your plusone manipulator, the stream object would have to have an internal flag to indicate that one should be added to all inserted values. The plusone manipulator would simply set that flag, and the code to handle stream insertion would check that flag before inserting numbers.

ReferenceURL : https://stackoverflow.com/questions/799599/c-custom-stream-manipulator-that-changes-next-item-on-stream

반응형