I'm relatively new to C++. Please excuse my terminology if it's incorrect. I tried searching around for an answer to my question, but I could not find it (probably because I couldn't phrase my question correctly). I'd appreciate it if someone could help me.
I am trying to write a class for creating strings that might contain the textual representation of objects or native types. Essentially, I have
private:
stringstream ss;
public:
template< typename T >
Message& operator<<( const T& value ) {
ss << value;
return *this;
}
The overloaded << operator takes some value and tries to stream it into a stringstream. I think my compiler is fine with this if the T is something like int or if the class T defines the method operator std::string(). However, if T is some type like vector<int>, then it no longer works because vector<int> doesn't define operator std::string().
Is there anyway I could perhaps overload this operator so that if T defines operator std::string(), then I print the textual representation, and if it doesn't, I just print its address?
Thanks.
This can be implemented by building upon the has_insertion_operator type trait described here: https://stackoverflow.com/a/5771273/4323
namespace has_insertion_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
template<typename T> any_t( T const& );
};
no operator<<( std::ostream const&, any_t const& );
yes& test( std::ostream& );
no test( no );
template<typename T>
struct has_insertion_operator {
static std::ostream &s;
static T const &t;
static bool const value = sizeof( test(s << t) ) == sizeof( yes );
};
}
template<typename T>
struct has_insertion_operator :
has_insertion_operator_impl::has_insertion_operator<T> {
};
Once we have that, the rest is relatively straightforward:
class Message
{
std::ostringstream ss;
public:
template< typename T >
typename std::enable_if<has_insertion_operator<T>::value, Message&>::type
operator<<( const T& value ) {
ss << value;
return *this;
}
template< typename T >
typename std::enable_if<!has_insertion_operator<T>::value, Message&>::type
operator<<( const T& value ) {
ss << &value;
return *this;
}
};
That is, if there is an insertion operator defined, print the value, otherwise print its address.
This does not rely on a conversion-to-std::string operator being defined--you only need to make sure your T instances are "printable" using operator << (typically implemented in the same scope where each T is defined, e.g. namespace or global scope).
Here's an example - using some custom traits for a conversion operator to std::string and the streaming operator:
#include <iostream>
#include <string>
template <class T>
struct traits
{
template <typename Q>
static auto hos(Q*) -> decltype(std::declval<const Q>().operator std::string());
static char hos(...);
constexpr static bool has_operator_string =
sizeof hos((T*){0}) != 1;
// ----
template <typename Q>
static auto isab(Q*) -> decltype(std::cout << std::declval<const Q>());
static char isab(...);
constexpr static bool is_streamable =
sizeof isab((T*){0}) != 1;
};
struct S
{
template <typename T>
typename std::enable_if<
traits<T>::has_operator_string,
S&>::type
operator<<(const T& value)
{
std::cout << "string() " << value.operator std::string() << '\n';
return *this;
}
template <typename T>
typename std::enable_if<!traits<T>::has_operator_string && traits<T>::is_streamable, S&>::type
operator<<(const T& value)
{
std::cout << "<< " << value << std::endl;
return *this;
}
template <typename T>
typename std::enable_if<
!traits<T>::has_operator_string &&
!traits<T>::is_streamable,
S&>::type
operator<<(const T& value)
{
std::cout << "T& @" << &value << std::endl;
return *this;
}
};
struct X
{
operator std::string() const { return "hi"; }
};
struct Y
{
};
int main()
{
std::cout << "> main()" << std::endl;
std::cout << "X() ";
S() << X();
Y y;
std::cout << "Y y; ";
S() << y;
std::cout << "Y() ";
S() << Y();
std::cout << "\"text\" ";
S() << "text";
std::cout << "< main()" << std::endl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With