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
を呼び出すたびに、Wordをdo_put
に再度設定するだけです。
私はこれについてニールバターワースに完全に同意しますが、使用している特定のケースでは、この恐ろしいハックを行うことができます。量産コードではこれを行わないでください。たくさんのバグがあります。 1つは、上記のワンライナーでのみ機能するため、基になるストリームの状態は変更されません。
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
を返すだけでさらに単純化できますが、_std::string
_を返すと一般的なケースに対応します。)
あなたはstreamstatesで遊ぶ必要があります。この件について次のリンクをブックマークしました。
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
<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
のデフォルトのコピーコンストラクターなどに依存していることを指摘するためにコードにコメントしました。堅牢な実装はおそらくこれらを定義します
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"
この簡単な例では、一時オブジェクトの作成とストリーミングは、誰かがすでに提案したように関数plusOne()を定義するよりもはるかに役立つようには見えません。しかし、次のように機能させたいとします。
std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
return o << po.i_ << " + 1 = " << (po.i_ + 1);
}
std::cout << PlusOne(num2); // => "1 + 1 = 2"