2009-04-28 16 views
48

पर अगले आइटम में परिवर्तन सी ++ में, हेक्साडेसिमल में एक नंबर मुद्रित करने के लिए आप यह करते हैं:सी ++ कस्टम धारा जोड़तोड़ कि धारा

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" 

हालांकि, कैसे मैं एक जोड़तोड़ कि, 'हेक्स' की तरह, आइटम को संशोधित धारा पर आने के लिए बना सकते हैं? एक साधारण उदाहरण के रूप में, मैं कैसे प्लस वन जोड़तोड़ यहां बनाई गई हैं ?:

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" 

उत्तर

62

को परिभाषित करेगा, सबसे पहले, आपको कुछ राज्य प्रत्येक स्ट्रीम में स्टोर करना होगा।

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

जगह में होने, आप पहले से ही कुछ राज्य सभी धाराओं में प्राप्त कर सकते हैं: आप कि समारोह iword और एक सूचकांक आप इसे पारित, xalloc द्वारा दिए गए के साथ क्या कर सकते हैं। अब, आपको केवल संबंधित आउटपुट ऑपरेशन में हुक करना होगा। संख्यात्मक आउटपुट एक पहलू द्वारा किया जाता है, क्योंकि यह संभावित रूप से लोकेल निर्भर है।तो आप

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

यदि आप चाहते हैं कि केवल अगले संख्या वृद्धि की जाती है, बस do_put की प्रत्येक कॉल के बाद फिर से 0 करने के लिए शब्द की स्थापना की।

+2

+1। यह पूरी तरह से किताब द्वारा (और जैसा कि आप देख सकते हैं, काफी जटिल) समाधान है। लेकिन मुझे आश्चर्य है कि यह एक फंक्शन प्लसोन() बनाने के लिए आसान (और संभवतः स्पष्ट) नहीं होगा जो एक तर्क लेता है और परिणाम देता है? –

+1

बहुत अच्छा प्रदर्शन! –

+0

धन्यवाद दोस्तों :) –

-1

hex, dec और oct manipulators बस अपने मौजूदा stream की basefield संपत्ति बदल जाते हैं।

इन मैनिपुलेटर्स के बारे में अधिक जानकारी के लिए C++ Reference देखें।

जैसा कि Neil Butterworth's answer में पोस्ट किया गया है, आपको स्ट्रीम में डाले गए भविष्य के मूल्यों को प्रभावित करने वाले मैनिपुलेटर्स को रखने के लिए मौजूदा स्ट्रीम क्लास का विस्तार करना होगा या अपना स्वयं का निर्माण करना होगा।

आपके plusone मैनिपुलेटर के उदाहरण में, स्ट्रीम ऑब्जेक्ट को इंगित करने के लिए एक आंतरिक ध्वज होना होगा कि किसी को सभी डाले गए मानों में जोड़ा जाना चाहिए। plusone मैनिपुलेटर बस उस ध्वज को सेट करेगा, और स्ट्रीम सम्मिलन को संभालने के लिए कोड संख्या डालने से पहले उस ध्वज की जांच करेगा।

+1

खेद -1 है - iostreams एक तानाना तंत्र होते हैं (xalloc(), iword(), pword()) जैसा कि litb द्वारा इंगित किया गया है। –

+2

माफी मांगने की कोई आवश्यकता नहीं है। मैं स्पष्ट रूप से गलत था, और मैं -1 को देखे बिना नहीं जानता था। इसे मेरे ध्यान में लाने के लिए धन्यवाद! –

11

मैं इस पर नील बटरवर्थ से पूरी तरह से सहमत हूं, हालांकि विशिष्ट मामले में आप इसका उपयोग कर रहे हैं, आप यह पूरी तरह से भयानक हैक कर सकते हैं। किसी भी उत्पादन कोड में ऐसा मत करो। इसमें बहुत सारी चीजें हैं। एक बात के लिए यह केवल आपके एक लाइनर में काम करता है, यह अंतर्निहित धारा की स्थिति को नहीं बदलता है।

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); 
} 
+0

आपने मुझे इसे हराया :) –

1

मैं <iomanip> उपयोग किए बिना अपने परीक्षण मामले के लिए एक सरल उपाय बनाया। मैं वादा नहीं कर सकता कि एक ही दृष्टिकोण वास्तविक जीवन में काम करेगा।

मूल दृष्टिकोण यह है कि cout << plusone एक अस्थायी सहायक वस्तु (PlusOnePlus) देता है, जो बदले में operator << अधिभारित करता है जो अतिरिक्त करता है।

मैं विंडोज पर जाँच की है:

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 के लिए डिफ़ॉल्ट प्रतिलिपि निर्माता (आदि) पर निर्भर कर रहा हूँ बाहर बात करने के लिए कोड टिप्पणी की। एक मजबूत कार्यान्वयन शायद इन

+0

चालाक, लेकिन ध्यान दें कि निम्नलिखित काम नहीं करेंगे: "ओएस << प्लसोन; ओएस << 41;" –

+0

मैं तर्क दे सकता हूं कि यह एक विशेषता है :-) हालांकि स्वीकार्य रूप से यह मानक तरीके से असंगत है कि मैनिपुलेटर्स संचालित होते हैं। –

+1

हाँ, मुझे संदेह है कि कोई भी C++ में एक और अधिक gratuitous असंगतता नोटिस होगा ...;) +1। –

1

आपको स्ट्रीमस्टेट के साथ खेलना होगा। मैं इस विषय पर निम्नलिखित लिंक बुकमार्क की गई:

मेसिएज 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 
+0

लाइब्रेरी लिंक मर चुका है। –

+0

@ बेनफुलन। वास्तव में।दुर्भाग्यवश पुस्तकालय अब और प्रतीत नहीं होता है। आईआईआरसी, मेरे पास कहीं ज़िप की एक प्रति है। और यहाँ यह है। यद्यपि आजकल बढ़ावा देने के लिए वापस गिरना बेहतर हो सकता है। –

0

litb का दृष्टिकोण है "सही तरीके से" और nece: मुझे अगर मैं गलत हूँ), यहाँ अपने मुख्य फ़ाइल की एक प्रतिलिपि है कि मैं गुमनामी से बचाने के लिए प्रबंधित किया है है जटिल सामान के लिए ssary, लेकिन इस तरह कुछ कुछ अच्छा हो सकता है। स्वाद के लिए गोपनीयता और दोस्ती जोड़ें।

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" 

इस सरल बनाने और एक अस्थायी वस्तु स्ट्रीमिंग उदाहरण में और अधिक उपयोगी एक समारोह प्लस वन() को परिभाषित करने के रूप में कोई पहले से ही सुझाव दिया की तुलना में प्रतीत नहीं होता। लेकिन आप इसे इस तरह से काम करना चाहता था लगता है:

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

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

यह आपके सवाल का सीधा जवाब नहीं है, लेकिन आपको नहीं लगता है कि एक सादे पुराने समारोह का उपयोग कर लागू करने के लिए दोनों आसान और अधिक स्पष्ट एक लिखने की तुलना में उपयोग करने के लिए है पूर्ण उड़ा हुआ मैनिपुलेटर?

#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 लौटने के बजाय, लेकिन लौटने एक std::string सामान्य स्थिति में कार्य करता है के द्वारा आसान बनाने में कर सकते हैं।)

संबंधित मुद्दे