Am struggling a lot to find how to do to use boost::any
to create a print function that can print any type using template first.
template
Very nice answer by Pawel Zubrycki (mentioning Björn Karlsson's book).
But the code has a few errors in the following lines:
// ...
o << *boost::any_cast(a); // should be: o << *boost::any_cast(&a);
// ...
a.streamer_->print(o, a); // should be: a.streamer_->print(o, a.o_);
Here's a corrected version of Pawel Zubrycki's answer that works (partially...)
#ifndef ANY_OUT_H
#define ANY_OUT_H
#include
#include
struct streamer {
virtual void print(std::ostream &o, const boost::any &a) const =0;
virtual streamer * clone() const = 0;
virtual ~streamer() {}
};
template
struct streamer_impl: streamer{
void print(std::ostream &o, const boost::any &a) const { o << *boost::any_cast(&a); }
streamer *clone() const { return new streamer_impl(); }
};
class any_out {
streamer *streamer_;
boost::any o_;
void swap(any_out & r){
std::swap(streamer_, r.streamer_);
std::swap(o_, r.o_);
}
public:
any_out(): streamer_(0) {}
template any_out(const T& value)
: streamer_(new streamer_impl()), o_(value) {}
any_out(const any_out& a)
: streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}
template
any_out & operator=(const T& r) {
any_out(r).swap(*this);
return *this;
}
~any_out() { delete streamer_; }
friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};
std::ostream &operator<<(std::ostream& o, const any_out & a) {
if(a.streamer_)
a.streamer_->print(o, a.o_);
return o;
}
#endif
This test-code works:
{
any_out a = 5;
std::cout << a << std::endl;
}
However!!!!
The following does not work:
int main()
{
char str[] = "mystring";
any_out a = str;
std::cout << a << std::endl;
a = "myconststring";
std::cout << a << std::endl;
}
Here nothing gets printed.
Why??
Well the type is messed up, in the following constructor
any_out(const T& value)
if we then instantiate streamer as
new streamer_impl()
Removing the reference from the constructor, i.e.
any_out(const T value)
... is one solution.
Another solution is to leave the reference and tweak the template instantiation of streamer_impl
. See below
Which brings as to the following recommened solution is:
#ifndef ANY_OUT_H
#define ANY_OUT_H
#include
#include
struct streamer {
virtual void print(std::ostream &o, const boost::any &a) const =0;
virtual streamer * clone() const = 0;
virtual ~streamer() {}
};
template
struct streamer_impl: streamer{
void print(std::ostream &o, const boost::any &a) const { o << boost::any_cast(a); }
streamer *clone() const { return new streamer_impl(); }
};
class any_out {
boost::any o_;
streamer *streamer_;
void swap(any_out & r){
std::swap(streamer_, r.streamer_);
std::swap(o_, r.o_);
}
public:
any_out(): streamer_(0) {}
template any_out(const T& value)
: o_(value),
#if 1
streamer_(new streamer_impl::type>)
#else
streamer_((o_.type() == typeid(const char *))
? static_cast(new streamer_impl)
: static_cast(new streamer_impl))
#endif
{
}
// template any_out(const T value)
// : o_(value),
// streamer_(new streamer_impl)
// {
// }
any_out(const any_out& a)
: o_(a.o_), streamer_(a.streamer_ ? a.streamer_->clone() : 0) {}
template
any_out & operator=(const T& r) {
any_out(r).swap(*this);
return *this;
}
~any_out() { delete streamer_; }
friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};
std::ostream &operator<<(std::ostream& o, const any_out & a) {
if(a.streamer_)
a.streamer_->print(o, a.o_);
return o;
}
#endif
The test-code that gave some trouble above, now works nicely (with the "recommeded solution"):
int main()
{
char str[] = "mystring";
any_out a = str;
std::cout << a << std::endl;
a = "myconststring";
std::cout << a << std::endl;
}