C ++ custom thread manipulator that modifies the next element in a thread

In C ++, to print a number in hexadecimal, you do the following:

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

I know that I can create a manipulator that simply adds material to the stream:

 std::ostream& windows_feed(std::ostream& out) { out << "\r\n"; return out; } std::cout << "Hello" << windows_feed; // => "Hello\r\n" 

However, how can I create a manipulator that, like "hex", modifies the elements in the stream? As a simple example, how can I create a plus manipulator here ?:

 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" 
+47
c ++ stream
Apr 28 '09 at 20:03
source share
7 answers

First, you need to keep some state in each thread. You can do this with the iword function and the pointer you pass to it given by 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; } 

After that, you can already get some state in all threads. Now you just need to connect to the corresponding output operation. Digital output is performed by the facet, as it potentially depends on the locale. So you can do

 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())); } }; 

Now you can test the material.

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

If you want only the next number to increase, just set the word 0 again after each do_put call.

+62
Apr 28 '09 at 9:23
source share

I completely agree with Neil Butterworth on this, however, in the specific case that you are using, you can do it completely horribly. Do not do this in any production code. It has a lot of mistakes. Firstly, it only works in one layer above; it does not change the state of the underlying stream.

 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); } 
+11
Apr 28 '09 at 20:37
source share

I created a simple solution for a test case without using <iomanip> . I can’t promise that the same approach will work in real life.

The basic approach is that cout << plusone returns a temporary helper object ( PlusOnePlus ), which in turn has an overloaded operator << that performs the addition.

I tested it on Windows:

 PlusOne plusone; cout << plusone << 41 

produces "42" as expected. Here is the code:

 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; } 

EDIT: commented out the code to indicate that I rely on the default copy constructor (etc.) for PlusOnePlus . A reliable implementation is likely to determine these

+1
Apr 28 '09 at 20:39
source share

You will have to play with file systems. I bookmarked the following related links:

Since the Maciej Sobczak library is no longer available on the Internet, and since the license allows me to do this (correct me if I am mistaken), here is a copy of its main file, which I managed to save from oblivion

 // 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 
+1
Apr 28 '09 at 21:31
source share
An approach

litb is the “right way” and is necessary for complex things, but something like this can be good enough. Add privacy and friendship to taste.

 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 does not look much more useful than defining the plusOne () function, as already mentioned. 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" 
0
Apr 29 '09 at 21:32
source share

This is not a direct answer to your question, but don't you think that using a simple old function is simpler and easier to use than writing a full-blown manipulator?

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

Using:

 cout << plusone(42); 

By the term "clear to use" I mean that the user does not need to ask himself: "Does this affect only the next element or all subsequent elements?" From the check it is obvious that only the argument of the function is affected.

(For the plusone() example, you can simplify even more by simply returning T instead, but returning std::string is for the general case.)

0
May 01 '09 at 3:21
source share

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

See the C ++ Reference for a more detailed description of these manipulators.

As stated in Neil Butterworth's Answer , you will need to expand existing stream classes or create your own to have manipulators that affect future values ​​inserted into the stream.

In the example of your plusone manipulator, the plusone object should have an internal flag indicating that it needs to be added to all inserted values. The plusone manifest plusone simply set this flag, and the code for handling the insert stream would check this flag before inserting numbers.

-one
Apr 28 '09 at 20:33
source share



All Articles