In C++, to print a number in hexadecimal you do this:
int num = 10;
std::cout << std::hex << num; // => 'a'
I know I can create a manipulator that just adds stuff to the stream like so:
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 items to come on the stream? As a simple example, how would I create the plusone 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"
First, you have to store some state into each stream. You can do that with the function iword and an index 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; }
Having that in place, you can already retrieve some state in all streams. Now, you just have to hook into the respective output operation. Numeric output is done by a facet, because it potentially is locale dependent. 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 stuff.
int main() {
// outputs: 11121011
cout.imbue(locale(locale(),new my_num_put));
cout << add_one << 10 << 11
<< add_none << 10 << 11;
}
If you want that only the next number is incremented, just set the word to 0 again after each call to do_put.
I totally agree with Neil Butterworth on this one, however in the specific case you are using you could do this totally horrible hack. Do not do this in any production code. It has lots of bugs. For one thing it only works in your one-liner 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);
}
It's not a direct answer to your question, but don't you think that using a plain old function is both simpler to implement and clearer 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();
}
Usage:
cout << plusone(42);
By "clear to use", I mean that the user doesn't need to ask themselves, "Does it affect only the next item, or all subsequent items?" It's obvious from inspection that only the argument of the function is affected.
(For the plusone() example, you could simplify even further by just returning a T instead, but returning a std::string serves the general case.)
I created a simple solution for your 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 auxiliary object (PlusOnePlus), which in turn has the overloaded operator << that performs the addition.
I've tested it on Windows:
PlusOne plusone;
cout << plusone << 41
produces "42", as expected. Here's 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 the code to point out that I'm relying on the default copy constructor (etc.) for PlusOnePlus. A robust implementation would probably define these
You'll have to play with streamstates. I've bookmarked the following links on the subject:
A discussion on boost ML
Maciej Sobczak's article on CUJ/DDJ
As Maciej Sobczak library is no longer available online, and as the licence permits me to do so, (correct me if I'm wrong), here is a copy of its main file that I've managed to salvage 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
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.
litb's approach is "the right way" and necessary for complicated stuff, 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 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"
Related
In C++, to print a number in hexadecimal you do this:
int num = 10;
std::cout << std::hex << num; // => 'a'
I know I can create a manipulator that just adds stuff to the stream like so:
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 items to come on the stream? As a simple example, how would I create the plusone 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"
First, you have to store some state into each stream. You can do that with the function iword and an index 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; }
Having that in place, you can already retrieve some state in all streams. Now, you just have to hook into the respective output operation. Numeric output is done by a facet, because it potentially is locale dependent. 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 stuff.
int main() {
// outputs: 11121011
cout.imbue(locale(locale(),new my_num_put));
cout << add_one << 10 << 11
<< add_none << 10 << 11;
}
If you want that only the next number is incremented, just set the word to 0 again after each call to do_put.
I totally agree with Neil Butterworth on this one, however in the specific case you are using you could do this totally horrible hack. Do not do this in any production code. It has lots of bugs. For one thing it only works in your one-liner 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);
}
It's not a direct answer to your question, but don't you think that using a plain old function is both simpler to implement and clearer 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();
}
Usage:
cout << plusone(42);
By "clear to use", I mean that the user doesn't need to ask themselves, "Does it affect only the next item, or all subsequent items?" It's obvious from inspection that only the argument of the function is affected.
(For the plusone() example, you could simplify even further by just returning a T instead, but returning a std::string serves the general case.)
I created a simple solution for your 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 auxiliary object (PlusOnePlus), which in turn has the overloaded operator << that performs the addition.
I've tested it on Windows:
PlusOne plusone;
cout << plusone << 41
produces "42", as expected. Here's 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 the code to point out that I'm relying on the default copy constructor (etc.) for PlusOnePlus. A robust implementation would probably define these
You'll have to play with streamstates. I've bookmarked the following links on the subject:
A discussion on boost ML
Maciej Sobczak's article on CUJ/DDJ
As Maciej Sobczak library is no longer available online, and as the licence permits me to do so, (correct me if I'm wrong), here is a copy of its main file that I've managed to salvage 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
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.
litb's approach is "the right way" and necessary for complicated stuff, 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 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"
I'm trying to write a stream manipulator with arguments.
I have class with 3 int's CDate(Year, Month, Day).
So I need to make manipulator date_format(const char*).
e.g. :
CDate a(2006, 5, 15);
cout <<"DATE IS : " << date_format("%Y-hello-%d-world-%m-something-%d%d") << a;
Output will be :
DATE IS : 2006-hello-15-world-5-something-1515
Guess i need to use that
ios_base & dummy_date_format_manipulator ( ios_base & x )
{
return x;
}
ios_base & ( * ( date_format ( const char * fmt ) ) )( ios_base & x )
{
return dummy_date_format_manipulator;
}
but i don't know how.
You can use pword array for this.
Every iostream in C++ has two arrays associated with it.
ios_base::iword - array of ints
ios_base::pword - array of void* pointers
You can store you own data in it. To obtain an index, that refers to an empty element in all iword and pword arrays you should use function std::ios_base::xalloc(). It returns int that you can use as an unique index in *word.
You should obtain that index once on the start-up, and than use it for all operations with *word.
Then programming your own manip will look like:
Manipulator function, that receives reference to ios_base object and pointer to the format string, simply stores that pointer in pword
iosObject.pword(index_from_xalloc) = formatString
Then overloaded operator << (>>) obtains format string from the iostream object in the same way. After that you just make a conversion referencing to the format.
Manipulators with arguments don't work the same as those without arguments! The are just classes with a suitable output operator which instead of outputting a value manipulate the stream's state. To manipulate the stream state you'll probably set up a suitabe value stored with an iword() or a pword() associated with the dtream and used by the output operator.
As chris suggested, I'd say that you should just use tm rather than your custom date class:
tm a{0, 0, 0, 15, 5, 2006 - 1900};
cout << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
If you must implement come custom functionality that cannot be accomplished with get_time and put_time then you'd probably want to use a tm member as part of your class so you could just extend the functionality that is already there:
class CDate{
tm m_date;
public:
CDate(int year, int month, int day): m_date{0, 0, 0, day, month, year - 1900}{}
const tm& getDate() const{return m_date;}
};
ostream& operator<<(ostream& lhs, const CDate& rhs){
auto date = rhs.getDate();
return lhs << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
}
You could then use CDate as follows:
CDate a(2006, 5, 15);
cout << "DATE IS:" << a;
EDIT:
After looking at your question again, I think that you have a misconception about how the insertion operator works, you cannot pass in both an object and a format: https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx
If you want to specify a format but still retain your CDate class, I'd again suggest the use of put_time:
cout << put_time(&a.getDate(), "%Y-hello-%d-world-%m-something-%d%d");
If you again insist on writing your own format accepting function you'll need to create a helper class that can be constructed inline and support that with the insertion operator:
class put_CDate{
const CDate* m_pCDate;
const char* m_szFormat;
public:
put_CDate(const CDate* pCDate, const char* szFormat) : m_pCDate(pCDate), m_szFormat(szFormat) {}
const CDate* getPCDate() const { return m_pCDate; }
const char* getSZFormat() const { return m_szFormat; }
};
ostream& operator<<(ostream& lhs, const put_CDate& rhs){
return lhs << put_time(&rhs.getPCDate()->getDate(), rhs.getSZFormat());
}
You could use this as follows:
cout << put_CDate(&a, "%Y-hello-%d-world-%m-something-%d%d") << endl;
Like Dietmar said you can push the params into the iword() but i find this kind of solution to be tedious and annoying..
I prefer to just install lambda functions as a iomanips and use them to directly call into the various classes methods or otherwise build the custom print in place. For that purpose I have created a simple 100 line template installer/helper class for mainpulators which can add a lambda function as the manipulator of any class..
So for your CDate you might define you manip as
std::ostream& dummy_date_format_manipulator (std::ostream& os)
{
CustomManip<CDate>::install(os,
[](std::ostream& oos, const CDate& a)
{
os << a.year()
<< "-hello-"
<< a.day()
<< "-world-"
<< a.month()
<< "-something-"
<< a.day() << a.day();
});
return os;
}
Then just direct the << op to use the manip installers print helper:
std::ostream& operator<<(std::ostream& os, const CDate& a)
{
CustomManip<CDate>::print(os, a);
return os;
}
And your basically done..
The mainp installer code and a fully working example is in my blog post at:
http://code-slim-jim.blogspot.jp/2015/04/creating-iomanip-for-class-easy-way.html
But to be nice.. here is the key part you want to put in a .h somewhere less all the printouts to demonstrate how it works:
//g++ -g --std=c++11 custom_class_manip.cpp
#include <iostream>
#include <ios>
#include <sstream>
#include <functional>
template <typename TYPE>
class CustomManip
{
private:
typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc;
struct CustomManipHandle
{
ManipFunc func_;
};
static int handleIndex()
{
// the id for this Custommaniputors params
// in the os_base parameter maps
static int index = std::ios_base::xalloc();
return index;
}
public:
static void install(std::ostream& os, ManipFunc func)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
// check if its installed on this ostream
if (handle == NULL)
{
// install it
handle = new CustomManipHandle();
os.pword(handleIndex()) = handle;
// install the callback so we can destroy it
os.register_callback (CustomManip<TYPE>::streamEvent,0);
}
handle->func_ = func;
}
static void uninstall(std::ios_base& os)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
//delete the installed Custommanipulator handle
if (handle != NULL)
{
os.pword(handleIndex()) = NULL;
delete handle;
}
}
static void streamEvent (std::ios::event ev,
std::ios_base& os,
int id)
{
switch (ev)
{
case os.erase_event:
uninstall(os);
break;
case os.copyfmt_event:
case os.imbue_event:
break;
}
}
static void print(std::ostream& os, const TYPE& data)
{
CustomManipHandle* handle
= static_cast<CustomManipHandle*>(os.pword(handleIndex()));
if (handle != NULL)
{
handle->func_(os, data);
return;
}
data.printDefault(os);
}
};
Of course if you really do need the parameters then use the CustomManip::make_installer(...) function to get it done but for that you will have to visit the blog..
Basically I want MyClass that holds a Hashmap that maps Field name(string) to ANY type of
Value.. For this purpose I wrote a separate MyField class that holds the type & value information..
This is what I have so far:
template <typename T>
class MyField {
T m_Value;
int m_Size;
}
struct MyClass {
std::map<string, MyField> fields; //ERROR!!!
}
But as you can see, the map declaration fails because I didn't provide the type parameter for MyField...
So I guess It has to be something like
std::map< string, MyField<int> > fields;
or
std::map< string, MyField<double> > fields;
But obviously this undermines my whole purpose, because the declared map can only hold MyField of a specific type.. I want a map that can hold ANY type of MyField clas..
Is there any way I can achieve this..?
This is plain in C++ 17. Use std::map + std::any + std::any_cast:
#include <map>
#include <string>
#include <any>
int main()
{
std::map<std::string, std::any> notebook;
std::string name{ "Pluto" };
int year = 2015;
notebook["PetName"] = name;
notebook["Born"] = year;
std::string name2 = std::any_cast<std::string>(notebook["PetName"]); // = "Pluto"
int year2 = std::any_cast<int>(notebook["Born"]); // = 2015
}
Blindy's answer is very good (+1), but just to complete the answer: there is another way to do it with no library, by using dynamic inheritance:
class MyFieldInterface
{
int m_Size; // of course use appropriate access level in the real code...
~MyFieldInterface() = default;
}
template <typename T>
class MyField : public MyFieldInterface {
T m_Value;
}
struct MyClass {
std::map<string, MyFieldInterface* > fields;
}
Pros:
it's familiar to any C++ coder
it don't force you to use Boost (in some contexts you are not allowed to);
Cons:
you have to allocate the objects on the heap/free store and use reference semantic instead of value semantic to manipulate them;
public inheritance exposed that way might lead to over-use of dynamic inheritance and a lot of long-term issues related to your types really being too inter-dependent;
a vector of pointers is problematic if it have to own the objects, as you have to manage destruction;
So use boost::any or boost::variant as default if you can, and consider this option only otherwise.
To fix that last cons point you could use smart pointers:
struct MyClass {
std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership
}
However there is still a potentially more problematic point:
It forces you to create the objects using new/delete (or make_unique/shared). This mean that the actual objects are created in the free store (the heap) at any location provided by the allocator (mostly the default one). Therefore, going though the list of objects very often is not as fast as it could be because of cache misses.
If you are concerned with performance of looping through this list very often as fast as possible (ignore the following if not), then you'd better use either boost::variant (if you already know all the concrete types you will use) OR use some kind of type-erased polymorphic container.
The idea is that the container would manage arrays of objects of the same type, but that still expose the same interface. That interface can be either a concept (using duck-typing techniques) or a dynamic interface (a base class like in my first example).
The advantage is that the container will keep same-type objects in separate vectors, so going through them is fast. Only going from one type to another is not.
Here is an example (the images are from there): http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html
However, this technique loose it's interest if you need to keep the order in which the objects are inserted.
In any way, there are several solutions possible, which depends a lot on your needs. If you have not enough experience with your case, I suggest using either the simple solution I first explained in my example or boost::any/variant.
As a complement to this answer, I want to point very good blog articles which summarize all C++ type-erasure techniques you could use, with comments and pros/cons:
http://talesofcpp.fusionfenix.com/post-16/episode-nine-erasing-the-concrete
http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/
Use either boost::variant (if you know the types you can store, it provides compile time support) or boost::any (for really any type -- but that's kind of unlikely to be the case).
http://www.boost.org/doc/libs/1_55_0/doc/html/variant/misc.html#variant.versus-any
Edit: I cannot emphasize enough that although rolling your own solution might seem cool, using a complete, proper implementation will save you a lot of headache in the long run. boost::any implements RHS copy constructors (C++11), both safe (typeid()) and unsafe (dumb casts) value retrievals, with const corectness, RHS operands and both pointer and value types.
That's true in general, but even more so for low level, base types you build your entire application on.
class AnyBase
{
public:
virtual ~AnyBase() = 0;
};
inline AnyBase::~AnyBase() {}
template<class T>
class Any : public AnyBase
{
public:
typedef T Type;
explicit Any(const Type& data) : data(data) {}
Any() {}
Type data;
};
std::map<std::string, std::unique_ptr<AnyBase>> anymap;
anymap["number"].reset(new Any<int>(5));
anymap["text"].reset(new Any<std::string>("5"));
// throws std::bad_cast if not really Any<int>
int value = dynamic_cast<Any<int>&>(*anymap["number"]).data;
C++17 has a std::variant type that has facilities for holding different types much better than a union.
For those not on C++17, boost::variant implements this same mechanism.
For those not using boost, https://github.com/mapbox/variant implements a much lighter version of variant for C++11 and C++14 that looks very promising, well documented, lightweight, and has plenty of usage examples.
You could also use a void* and cast the value back to the correct type using reinterpret_cast. Its a technique often used in C in callbacks.
#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdint> // Needed for intptr_t
using namespace std;
enum TypeID {
TYPE_INT,
TYPE_CHAR_PTR,
TYPE_MYFIELD
};
struct MyField {
int typeId;
void * data;
};
int main() {
std::unordered_map<std::string, MyField> map;
MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) };
char cstr[] = "Jolly good";
MyField aCString = { TYPE_CHAR_PTR, cstr };
MyField aStruct = { TYPE_MYFIELD, &anInt };
map.emplace( "Int", anInt );
map.emplace( "C String", aCString );
map.emplace( "MyField" , aStruct );
int intval = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data));
const char *cstr2 = reinterpret_cast<const char *>( map["C String"].data );
MyField* myStruct = reinterpret_cast<MyField*>( map["MyField"].data );
cout << intval << '\n'
<< cstr << '\n'
<< myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl;
}
This is a naive way of doing it. Of course, you can add wrappers to void the some boiler plate code.
#include <iostream>
#include <memory>
#include <map>
#include <vector>
#include <cassert>
struct IObject
{
virtual ~IObject() = default;
};
template<class T>
class Object final : public IObject
{
public:
Object(T t_content) : m_context(t_content){}
~Object() = default;
const T& get() const
{
return m_context;
}
private:
T m_context;
};
struct MyClass
{
std::map<std::string, std::unique_ptr<IObject>> m_fields;
};
int main()
{
MyClass yourClass;
// Content as scalar
yourClass.m_fields["scalar"] = std::make_unique<Object<int>>(35);
// Content as vector
std::vector<double> v{ 3.1, 0.042 };
yourClass.m_fields["vector"] = std::make_unique<Object<std::vector<double>>>(v);
auto scalar = dynamic_cast<Object<int>*>(yourClass.m_fields["scalar"].get())->get();
assert(scalar == 35);
auto vector_ = dynamic_cast<Object<std::vector<double>>*>(yourClass.m_fields["vector"].get())->get();
assert(v == vector_);
return 0;
}
Work in progress. The advantage this method has is that you don't have to cast anything when doing assignment, or any of the features listed below.
As of now it can:
store non-container literal types (const char*, double, int, float, char, bool)
output value for corresponding key with ostream operator
reassign the value of an existing key
add a new key:value pair using the append method only, key cannot be the same, or else you get an error message
add literals of the same type with the + operator
In the code, I have demonstrated in the main function what it can currently do.
/*
This program demonstrates a map of arbitrary literal types implemented in C++17, using any.
*/
#include <vector>
#include <any>
#include <utility>
#include <iostream>
using namespace std;
class ArbMap
{
public:
ArbMap() : vec({}), None("None") {} //default constructor
ArbMap(const vector < pair<any,any> > &x) //parametrized constructor, takes in a vector of pairs
: vec(x), None("None") {}
//our conversion function, this time we pass in a reference
//to a string, which will get updated depending on which
//cast was successful. Trying to return values is ill-advised
//because this function is recursive, so passing a reference
//was the next logical solution
void elem(any &x, string &temp, int num=0 )
{
try
{
switch (num)
{
case 0:
any_cast<int>(x);
temp = "i";
break;
case 1:
any_cast<double>(x);
temp = "d";
break;
case 2:
any_cast<const char*>(x);
temp = "cc";
break;
case 3:
any_cast<char>(x);
temp = "c";
break;
case 4:
any_cast<bool>(x);
temp = "b";
break;
case 5:
any_cast<string>(x);
temp = "s";
break;
}
}
catch(const bad_cast& e)
{
elem(x,temp,++num);
}
}
//returns size of vector of pairs
size_t size()
{
return vec.size();
}
/* Uses linear search to find key, then tries to cast
all the elements into the appropriate type. */
any& operator[](any key)
{
ArbMap temp;
string stemp;
for (size_t i = 0; i<vec.size(); ++i)
{
temp.elem(vec[i].first,stemp);
if (stemp=="i")
{
try
{
any_cast<int>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<int>(key)==any_cast<int>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="d")
{
try
{
any_cast<double>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<double>(key)==any_cast<double>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="cc")
{
try
{
any_cast<const char*>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<const char*>(key)==any_cast<const char*>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="c")
{
try
{
any_cast<char>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<char>(key)==any_cast<char>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="b")
{
try
{
any_cast<bool>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<bool>(key)==any_cast<bool>(vec[i].first))
{
return vec[i].second;
}
}
}
//vec.push_back({key,None});
throw -1;
//return None;
}
void print();
void append(any key, any value);
private:
vector < pair<any,any> > vec;
any None;
};
ostream& operator<<(ostream& out, any a)
{
ArbMap temp; //should be updated to be a smart pointer?
string stemp;
temp.elem(a,stemp); //stemp will get updated in the elem function
//The "if else-if ladder" for casting types
if (stemp=="i") out << any_cast<int>(a);
else if (stemp=="d") out << any_cast<double>(a);
else if (stemp=="cc") out << any_cast<const char*>(a);
else if (stemp=="c") out << any_cast<char>(a);
else if (stemp=="b")
{
if (any_cast<bool>(a)==1)
out << "true";
else
out << "false";
}
else if (stemp=="s") out << any_cast<string>(a);
return out;
}
any operator+(any val1, any val2)
{
ArbMap temp;
string stemp1, stemp2;
temp.elem(val1,stemp1);
temp.elem(val2,stemp2);
try
{
if (stemp1 != stemp2)
throw -1;
if (stemp1 == "i")
{
return any_cast<int>(val1)+any_cast<int>(val2);
}
else if (stemp1 == "d")
{
return any_cast<double>(val1)+any_cast<double>(val2);
}
else if (stemp1 == "cc")
{
return string(any_cast<const char*>(val1))+string(any_cast<const char*>(val2));
}
else if (stemp1 == "c")
{
return string{any_cast<char>(val1)}+string{any_cast<char>(val2)};
}
else if (stemp1=="b")
{
return static_cast<bool>(any_cast<bool>(val1)+any_cast<bool>(val2));
}
}
catch (int err)
{
cout << "Bad cast! Operands must be of the same 'type'.\n";
}
return val1;
}
void ArbMap::print()
{
cout << '\n';
for (size_t i = 0; i<vec.size(); ++i)
{
cout << vec[i].first << ": " << vec[i].second << '\n';
}
cout << '\n';
}
void ArbMap::append(any key, any value)
{
try
{
(*this)[key];
throw "Already exists!";
}
catch(int error)
{
vec.push_back({key,value});
}
catch(const char* error)
{
cout << "ArbMap::append failed, key already exists!\n";
}
}
int main() {
ArbMap s({{1,2},{"aaa",1.2},{'c',33.3},{"what","this is awesome"}, {true, false}});
cout << s[1] << '\n' << s["aaa"] << '\n' << s['c']
<< '\n' << s["what"] << '\n'
//Uncomment the line below and runtime error will occur, as
//entry is not in the dictionary
// << s["not in the dictionary bro"] << '\n'
<< s[true] << '\n';
s.print();
s[1] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[2.3] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[1] = 1.2;
s.print();
s.append(2.4,1.2);
//Operator +
cout << s[1]+s[2.4] << '\n';
cout << s["what"] + s[2.3] << '\n';
s.append('d','a');
cout << s['c'] << '\n';
cout << s[2.4]+ s["aaa"]+ s['c'] + s['c'] + s['c'] << '\n';
cout << s[true]+s[true] << '\n';
return 0;
}
I'm making a logger and I wish to have some kind of stream-like happenings going on, ideally doing CLogger << "Testing, " << 1 << ",2,3\n"; instead of CLogger->log("Testing, %i,2,3", 1);
My question is how would I do this? I don't want to directly create a stream to stdout as I want to use my own method which includes writing files and such. I've considered overloading with a certain struct that'd flush the current stream buffer to a method, but I'd have to do CLogger << flush << "Test!\n"; which is kind of odd.
Does anybody know how to do this?
If all that you need is directing certain log messages to files, have you considered std::ofstream?
Otherwise, I like to derive my logging class from std::ostream, so I get all of the stream goodness. The trick is to put all of your application-specific code in the associated streambuf class. Consider:
#include <iostream>
#include <sstream>
class CLogger : public std::ostream {
private:
class CLogBuf : public std::stringbuf {
private:
// or whatever you need for your application
std::string m_marker;
public:
CLogBuf(const std::string& marker) : m_marker(marker) { }
~CLogBuf() { pubsync(); }
int sync() {
std::cout << m_marker << ": " << str();
str("");
return std::cout?0:-1;
}
};
public:
// Other constructors could specify filename, etc
// just remember to pass whatever you need to CLogBuf
CLogger(const std::string& marker) : std::ostream(new CLogBuf(marker)) {}
~CLogger() { delete rdbuf(); }
};
int main()
{
CLogger hi("hello");
CLogger bye("goodbye");
hi << "hello, world" << std::endl;
hi << "Oops, forgot to flush.\n";
bye << "goodbye, cruel world\n" << std::flush;
bye << "Cough, cough.\n";
}
Notes:
The CLogger constructor can take whatever parameters you need to use -- a filename, an output language, a pointer to the underlying log data, whatever. Just pass the data onto the CLogBuf class.
The CLogBuf's sync() is automatically called during in response to std::flush.
Check out operator <<, which is what STL's streams overload.
class CLogger
{
public:
CLogger& operator << (const std::string& _rhs)
{
// work with it here
return *this;
}; // eo operator <<
}; // eo class CLogger
EDIT:
See this page that outlines how std::ostream overloads operator << for different types:
http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C/
Implement a proxy object that gives you operator<< and pass an ownership marker to the returned proxy object. When an object with the ownership marker dies, you flush the stream.
An easy way to do this would be to wrap ostringstream in an auto_ptr in your proxy and flushing to your logger when the auto_ptr is not null in the proxy's d-tor.
That'll give you the formatting possible with ostream, but still result in only one call to your logger, which I thought was the real problem.
Think of something like this:
class CLoggingProxy
{
public:
template <class T>
CLoggingProxy operator<<( const T& rhs )
{
if ( stream )
*stream << rhs;
return *this;
}
~CLoggingProxy()
{
if ( stream )
logger->log(stream->str());
}
private:
std::auto_ptr<std::ostringstream> stream;
CLogger* logger;
friend class CLogger;
CLoggingProxy( CLogger* logger ) // call this e.g. from the logger to "start" input
: stream(new std::ostringstream), logger(logger) {}
};
All of the operator<<() functions are defined on the class ostream, which you can inherit from and implement its methods.
I'm just going to copy-paste my current implementation of this below, it does all you need (and handles things like std::endl and the like). AMBROSIA_DEBUGis macro defined in debug builds, so in theory, every call to this output class should be omitted in release builds (haven't checked though, but seems logical overhead is kept to a minimum. The functionality is based on QDebug functionality, plus a little addition of mine debugLevel, which would allow you to filter debug messages by hand in your code depending on a runtime parameter. Right now it also adds the same amount of spaces before each message.
// C++ includes
#include <iostream>
#include <string>
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
static int debugLevel;
const static int maxDebugLevel = 9;
#endif
class Debug
{
public:
#ifdef AMBROSIA_DEBUG
Debug( const int level = 0 )
: m_output( level <= debugLevel ),
m_outputSpaces( true ),
m_spaces( std::string(level, ' ') )
#else
Debug( const int )
#endif // AMBROSIA_DEBUG
{}
template<typename T>
#ifdef AMBROSIA_DEBUG
Debug& operator<<( const T &output )
{
if( m_output )
{
if( m_outputSpaces )
{
m_outputSpaces = false;
std::cerr << m_spaces;
}
std::cerr << output;
}
#else
Debug& operator<<( const T & )
{
#endif // AMBROSIA_DEBUG
return *this;
}
// for std::endl and other manipulators
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
Debug& operator<<( STRFUNC func )
{
if( m_output )
func(std::cerr);
#else
Debug& operator<<( STRFUNC )
{
#endif // AMBROSIA_DEBUG
return *this;
}
private:
#ifdef AMBROSIA_DEBUG
bool m_output;
bool m_outputSpaces;
std::string m_spaces;
#endif // AMBROSIA_DEBUG
};
Example usage:
int main()
{
debugLevel = 9; // highest allowed in my app...
Debug(4) << "This message should have an indentation of 4 spaces." << endl;
Debug(8) << "This is a level 8 debug message.\n";
return 0;
}
Continuing from the question that I asked here: C++ multi-dimensional data handling
In my example: I have many Chips, each Chip has many Registers, each Register has many Cells, and each Cell has many Transistors. I asked whether to use one complex STL container for them, or to implement full classes for them. And, as advised, I chose to implement full classes for them. I have:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
};
class Register
{
map<CellLocation, Cell> CellsPerLocation;
};
// etc..
Now, I need to fill the data to the classes, and I can't decide: Should reading the data be responsibility of these classes, or should they just wrap the containers and the reading will be done outside.
I mean I have to choose one of the following: Either:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
public:
void AddRegisterPerLocation(RegisterLocation, Register);
};
void ReadChipData(Chip & chip)
{
for (RegisterLocation loc = 0; loc < 10; loc++)
{
Register reg;
ReadReg(reg);
chip.AddRegisterPerLocation(loc, reg);
}
}
void ReadReg(Register & reg)
{
for (CellLocation loc = 0; loc < 10; loc++)
{
Cell cell;
ReadCell(cell);
reg.AddRegisterPerLocation(loc, cell);
}
}
//etc...
Or:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
public:
void ReadData();
};
void Chip::ReadData()
{
for (RegisterLocation loc = 0; loc < 10; loc++)
{
Register reg;
reg.ReadData();
RegistersPerLocation[loc] = reg;
}
}
//etc...
void ReadChipData(Chip & chip)
{
chip.ReadData();
}
Thank you.
If you are thinking of tying the reader/writer to the domain objects in order to follow the principle of encapsulation, you are correct to a certain extent. But remember: You bind not just any action, but a valid behavior. Valid as in makes sense for the object in the domain.
Another thing to keep in mind is separation of concerns. Serializability is not a Chip's intrinsic behavior -- modeling that into the domain object would be unfair IMO. YMMV.
Separate the reading(and writing) from the classes. As the library does. Expose iterators if you have to. And you can overload the '<<' and '>>' operators for syntactic sugar ;)
A minor nit on the classes -- a template based approach looks so promising.
Here's some code I cooked up: you can try the following as well.
(I've successfully compiled and run this on a MS VS2005 but check it out on your system. Also, can someone fix the tabbing -- feeling too lazy to do this :P)
/*+-----------8<----------------------------8<-----------+*/
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
#include <iterator>
/* mother template */
template<class _Item>
struct Hardware
{
Hardware() : _myCont(2 + ::rand() % 5) {}
private:
typename vector<_Item> _myCont;
// i/o friends
template<class _Item>
friend ostream& operator<<(ostream& output,
const Hardware<_Item>& me);
template<class _Item>
friend istream& operator>>(istream& in,
const Hardware<_Item>& me);
};
/* actual domain objects */
/* base object */
struct Transistor {
};
/* built objects */
typedef Hardware<Transistor> Cell;
typedef Hardware<Cell> Register;
typedef Hardware<Register> Chip;
/* poorman's introspection utility */
template<class T>
const char *who() { return ""; }
template<>
const char *who<Transistor>() { return "Transistor"; }
template<>
const char *who<Cell>() { return "Cell"; }
template<>
const char *who<Register>() { return "Register"; }
template<>
const char *who<Chip>() { return "Chip"; }
/* writer/serialize out */
template<class T>
ostream& operator<<(ostream& out, const Hardware<T>& hw) {
// whatever you need to do to write
// os << chip works fine, because you will provide a specialization
out << "[ " << ::who<Hardware<T>>() << " ]\n\t";
std::copy(hw._myCont.begin(), hw._myCont.end(),
std::ostream_iterator< T >(std::cout, "\n\t"));
return out;
}
/* specialize for base object */
ostream& operator<< (ostream& out, const Transistor& hw) {
out << "[ " << ::who<Transistor>() << " ]\n";
return out;
}
/* reader/serialize in */
template<class T>
istream& operator>>(istream& in, const Hardware<T>& hw) {
// whatever you need to do to read
// similarly in >> chip works fine,
return in;
}
// driver showing relationships
// Chip -> Register -> Cell -> Transistor
int main() {
Transistor t;
std::cout << t << std::endl;
Cell ce;
std::cout << ce << std::endl;
Register r;
std::cout << r << std::endl;
Chip C;
std::cout << C << std::endl;
}
/*+-----------8<----------------------------8<-----------+*/
Caveat: Haven't tested, so there may be quite a few compiler errors/warnings. But this should give you an idea of what I am trying to say.
Making classes responsible for their serialization is better - once you change the class fields you have to change the same class serializeation method, not some reader/writer code over there.