Compile time overloading for ostream operator - c++

I am trying to introduce the overloading mechanism based on rank of the object.
I managed to implement simple example based on other post and it works for simple types:
https://coliru.stacked-crooked.com/a/8129de0ae8a71af1
Now I would like to do sth similar for custom type:
#include <iostream>
#include <type_traits>
#include <sstream>
class Foo {
};
template < class T,
typename std::enable_if <(std::rank<T>::value == 0), int>::type = 0 >
void f(std::ostream& os, const T& value)
{
os << "rank: 0" << std::endl;
}
template< class T,
typename std::enable_if<(std::rank<T>::value == 1), int>::type = 0 >
void f(std::ostream& os, const T& value)
{
os << "rank: 1" << std::endl;
}
template <class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f<decltype(foo)>(os, foo);
return os;
}
int main()
{
Foo foo0;
Foo foo1[5];
//std::cout << std::rank<decltype(foo0)>{} << "\n"; // 0
//std::cout << std::rank<decltype(foo1)>{} << "\n"; // 1
// correct
f<decltype(foo0)>(std::cout, foo0); //prints: rank: 0
f<decltype(foo1)>(std::cout, foo1); //rank: 1
// invalid
std::cout << foo0; //prints: rank: 0
std::cout << foo1; //rank: 0
return 0;
}
https://coliru.stacked-crooked.com/view?id=cf91cec14a111f70
When I am calling functions directly, I am receiving valid results, but when I am doing it by << each time I am getting 0.
Could you explain what I am doing wrong?

You don't need to use decltype here, just use T:
template<class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f< T >(os, foo);
return os;
}
but when using decltype you will have to remove reference from foo type:
template<class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f< std::remove_reference_t< decltype(foo) > >(os, foo);
return os;
}
When template type deduction works T is deduced to be Foo [5] - what rank supports. But when you do decltype(foo), it returns type also with const & - so you have const Foo (&)[4]. rank for that thing just doesn't work.

Related

Why SFINAE has different behavior with gcc <11 vs >12?

I saw this example of using SFINAE to check if a type is streamable here. However, I noticed that it is not portable, i.e. returns different results for templated types with different compilers. I'd be glad for any tips to understand the problem here.
The code below returns true, false with any version of clang++ and GCC 12 or higher, but true, true with earlier versions of GCC.
You can try it online here.
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T, typename dummy = void>
struct is_printable : std::false_type {};
template <typename T>
struct is_printable<
T, typename std::enable_if_t<std::is_same_v<
std::remove_reference_t<decltype(std::cout << std::declval<T>())>,
std::ostream>>> : std::true_type {};
template <typename T>
inline constexpr bool is_printable_v = is_printable<T>::value;
struct C {};
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "C";
return os;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
for (const auto& el : v) {
os << el;
}
return os;
}
int main(int argc, const char* argv[]) {
std::cout << std::boolalpha;
std::cout << is_printable_v<C> << std::endl;
std::cout << is_printable_v<std::vector<int>> << std::endl;
return 0;
}
operator<<(std::ostream& os, const std::vector<T>& v) won't be found by ADL (for std::vector<int>, it would for std::vector<C>) (and so need to be declared before usage to be usable).
That is why correct answer is true, false.
previous version of gcc misbehave on this.
Note: It is discouraged to overload operator for types which you don't own. std might in the future add that overload, and you will have ODR (One definition rule) violation. Same if another library does the same wrong thing than you.
Adding a forwarding declaration help:
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v);
Full code:
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v);
template <typename T, typename dummy = void>
struct is_printable : std::false_type {};
template <typename T>
struct is_printable<
T, typename std::enable_if_t<std::is_same_v<
std::remove_reference_t<decltype(std::cout << std::declval<T>())>,
std::ostream>>> : std::true_type {};
template <typename T>
inline constexpr bool is_printable_v = is_printable<T>::value;
struct C {};
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "C";
return os;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
for (const auto& el : v) {
os << el;
}
return os;
}
int main(int argc, const char* argv[]) {
std::cout << std::boolalpha;
std::cout << is_printable_v<C> << std::endl;
std::cout << is_printable_v<std::vector<int>> << std::endl;
return 0;
}
Demo : https://godbolt.org/z/qKz537TPr
I know I didn't answer "why both version behave differently" but I think it may help :)

Google test and operator<< overload for STL types

Why following code produces compilation error?
#include <iostream>
#include "gtest/gtest.h"
#include <utility>
namespace A {
//overloading operator << for std::pair
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
TEST(TestA, testA) {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
EXPECT_EQ(0, 0) << pair1; //compiles
EXPECT_EQ(0, 0) << pair2; //doesn't compile
}
}
I use Visual Studio 2015. Error text is:
Error C2679 binary '<<': no operator found which takes a right-hand
operand of type 'const std::pair' (or there is no acceptable
conversion) ...\gtest\gtest-message.h 131
How changing user-defined type to built-in type makes a difference?
Upd. Thanks to #Kerrek SB, error is explained. However, now there is another question: How should I overload operator<< for std::pair to be able to use it like in my code?
When one writes something like EXPECT_EQ(e1,e2) << some_pair, the template function
template<typename T>
::testing::Message::operator<<
is being instantiated. Only inside this function user-defined operator<< is being called. Because function resides in another namespace (not in A) user-defined operator<< can't be found.
Solution is easy. Gtest offers function ::testing::PrintToString which accepts STL containers. So the code should be like this (I changed it to make sense):
EXPECT_TRUE(some_predicate(pair2)) << ::testing::PrintToString(pair2);
I took your answer and turned it into a minimal example, removing the dependence on the Google code:
#include <iostream>
namespace A {
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
void test() {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
std::cout << pair1 << std::endl; // compiles
std::cout << pair2 << std::endl; // also compiles
}
}
int main(int argc, char *argv[]) {
A::test();
}
This actually compiles fine for me, with the output
pair:{0, 0}
pair:{0, 0}
So, I cannot reproduce your problem. However, #KerrekSB has identified a good reason for your problem. I suspect the difference might lie in the fact that your code calls operator<< inside TEST, some type of macro defined by the Google Test package, while my more minimal example replaces this with a function in the namespace A, perhaps changing the name lookup?
You need to define operator << for std::pair in std namespace for gtest to find it.
The code as follows compiles fine:
#include <iostream>
#include "gtest/gtest.h"
#include <utility>
namespace std
{
//overloading operator << for std::pair
template<typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
}
namespace A {
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
TEST(TestA, testA) {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
EXPECT_EQ(0, 0) << pair1; //compiles
EXPECT_EQ(0, 0) << pair2; //compiles!
}
}

Print pair without introducing wrappers

I know that I could introduce wrapper(proxy class) to wrap pair and add overloaded << operator, but I I'am wondering why introducing '<<' operator for std namespace like below do not work?
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
namespace std{
ostream& operator<<(ostream& os, pair<int, int>&);
}
std::ostream& std::operator<<(std::ostream& os, std::pair<int, int>& pi){
os << pi.first <<", " << pi.second;
return os;
}
int main(){
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
std::cout << pi.front<<std::endl;
}
This is illegal:
namespace std{
ostream& operator<<(ostream& os, pair<int, int>&);
}
you may only specialise a template class for function in the std namespace for user defined types.
You may not add overloads for user defined types to the std namespace.
This is an overload in namespace std.
anticipating:
but what is the correct way?
This is:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
template<class T>
struct tuple_printer;
template<class T>
auto tuple_print(const T& t) {
return tuple_printer<std::decay_t<T>>(t);
}
template<class T>
struct tuple_printer
{
tuple_printer(const T& t) : _t(t) {}
void operator()(std::ostream& os) const {
os << _t;
}
const T& _t;
};
template<class X, class Y>
struct tuple_printer<std::pair<X, Y>>
{
using arg_type = std::pair<X, Y>;
tuple_printer(const arg_type& t) : _t(t) {}
void operator()(std::ostream& os) const {
os << '(' << tuple_print(_t.first) << ", " << tuple_print(_t.second) << ')';
}
const arg_type& _t;
};
template<class T, class A>
struct tuple_printer<std::vector<T, A>>
{
using arg_type = std::vector<T, A>;
tuple_printer(const arg_type& t) : _t(t) {}
void operator()(std::ostream& os) const {
auto sep = " ";
os << '[';
for (const auto& e : _t) {
os << sep << tuple_print(e);
sep = ", ";
}
os << " ]";
}
const arg_type& _t;
};
template<class T>
std::ostream& operator<<(std::ostream& os, const tuple_printer<T>& tp){
tp(os);
return os;
}
int main(){
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
pi.push_back(std::make_pair(3, 4));
std::cout << tuple_print(pi.front()) << std::endl;
std::cout << tuple_print(pi) << std::endl;
return 0;
}
expected output:
(1, 2)
[ (1, 2), (3, 4) ]
Perfect, eh?
You have a simple typo in your code:
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
std::cout << pi.front<<std::endl;
The last line should read:
std::cout << pi.front() <<std::endl;
note the difference between std::pair<X,Y>::front and std::vector<X>::front().
As a side note, you should probably make your operator<< accept a const pair.

How do I overload operators for some specific data types?

Consider this code
#include <bits/stdc++.h>
using namespace std;
struct foo {
template <typename T>
foo& operator<< (const T& var) {
cout << var;
return *this;
}
} bar;
int main() {
bar << 1 << '\n';
bar << 1.2 << '\n';
return 0;
}
I want to overload << operator only for integer data types. (int16_t, int32_t, int64_t)
How can I do that?
You can use SFINAE to ensure that T is an integral type:
template <typename T>
std::enable_if_t<std::is_integral<T>::value, foo&>
operator<< (const T& var) {
cout << var;
return *this;
}
Alternatively, you can use static_assert:
template <typename T>
foo& operator<< (const T& var) {
static_assert(std::is_integral<T>::value, "T must be an integral type");
cout << var;
return *this;
}
Note that you should not #include anything from the bits folder, that's non-portable.
If you want separate overloads for integral and floating point types, you can make two overloads and select with SFINAE:
template <typename T>
std::enable_if_t<std::is_integral<T>::value, foo&>
operator<< (const T& var) {
cout << "int " << var;
return *this;
}
template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, foo&>
operator<< (const T& var) {
cout << "float " << var;
return *this;
}
When we get Concepts you'll be able to write something like this:
template <Integral T>
// ^^^^^^^^ Integral rather than typename
foo& operator<< (const T& var) {
cout << var;
return *this;
}

How to implement a << interface in C++?

I'm working on a tiny compiler, and I use boost::variant<bool, ClassA> infoto store the information of each node.
boost::variant will automatically call the correct << operator of a specific type when I call std::cout<< node.info;
However, since the built-in formatting function of ostream doesn't satisfy my requirement(print #t instead of 1 if node.info==true and print "string" instead of string), new types of bool/string should be introduced.
I want to implement a template class Wrapper<T>, which behaves just like T(because there are lots of old code) and provides the interface of <<.
At first, the following version was implemented:
template<typename T> class Wrapper : public T
{
public:
template<typename ... U> Wrapper(const U& ... a):T(a...) {}
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
This version works well for std::string, but when T=bool, since built-in types cannot be inherited, an error will raise.
My current workaround is to use partial specialization:
template<typename T, bool ISCLASS= std::is_class<T>::value> class Wrapper;
template<typename T> class Wrapper<T, false>
{
private:
T inner;
public:
template<typename ... U> Wrapper(const U& ... a): inner(a...) {}
//Wrap the operators (= + += ...)
template<typename U> Wrapper<T> operator !() { Wrapper<T> res(*this); res.inner=!res.inner; return res; }
operator T() const{ return inner; }
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
template<typename T> class Wrapper<T, true> : public T
{
public:
template<typename ... U> Wrapper(const U& ... a):T(a...) {}
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
Obviously it is not a perfect solution because I have to wrap every operators of bool or any other built-in types.
Any help would be appreciated.
Thanks.
Could we consider something simpler?
Create a simple wrapper, using reference or pointer.
template <class T>
struct MyFormattingWrapper
{
const T& nucleus;
};
And then a factory function for it.
template <class T>
MyFormattingWrapper<T> my_formatting(const T& n)
{
return MyFormattingWrapper<T>{ n };
}
And then, you can specialize the formatting for each type.
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<int>& w)
{
return o << "int:" << w.nucleus;
}
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<std::string>& w)
{
return o << "std::string:" << w.nucleus;
}
int main()
{
std::cout << my_formatting(123) << std::endl;
std::cout << my_formatting(std::string{ "abc" }) << std::endl;
}
Update:
C-string may be a special case. But it is not difficult.
struct MyFormattingWrapper_c_string
{
const char* const nucleus;
};
MyFormattingWrapper_c_string my_formatting(const char* n)
{
return MyFormattingWrapper_c_string{ n };
}
MyFormattingWrapper_c_string my_formatting(char* n)
{
return MyFormattingWrapper_c_string{ n };
}
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper_c_string& w)
{
return o << "c-string:" << w.nucleus;
}
Nicky C's answer is great, but has an issue with partial specialization of functions not being OK. This means you can't produce a version that works on general vectors like this:
template<typename T>
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<std::vector<T>>& vec)
{
o << "vec:[ "
for(auto v : vec) {
o<<my_formatting(v);
o<<" ";
}
return o<<"]"
}
You can get around this by putting the core of the specialized output into the MyFormattingWrapper classes and having only one operator<<
// The default one
template <class T> struct MyFormattingWrapper {
const T& nucleus;
ostream& output(ostream & os) {
return os<<nucleus;
}
};
// Specialized for string
template <> struct MyFormattingWrapper<std::string> {
const std::string& nucleus;
ostream& output(ostream & os) {
return os<<"string:"<<nucleus;
}
};
// Specialized for vector
template <class T> struct MyFormattingWrapper<std::vector<T>> {
const std::vector<T>& nucleus;
ostream& output(ostream & os) {
os<<"vec:[";
for(auto & v: nucleus) {
os<<my_formatting(v)<<" ";
}
return os<<"]";
}
};
// Now there's just one of these, so partial template
// specialization doesn't cause us any problems
template<typename T>
std::ostream& operator << (std::ostream& os, const MyFormattingWrapper<T>& w) {
return w.output(os);
}
Perhaps I better make the follow-up regarding boost::variant another answer.
Firstly, learning from #MichaelAnderson, and considering the interoperability with boost::variant, I would like to improve the design of the wrapper. We add a constructor to enable type conversion for from the nucleus type to the wrapper type.
template <class T>
class MyFormatting;
template <class T>
MyFormatting<T> my_formatting(const T& n)
{
return MyFormatting <T>{n};
}
// int
template <>
class MyFormatting<int>
{
private:
const int& nucleus;
public:
MyFormatting(const int& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "int:" << w.nucleus;
}
};
// std::string
template <>
class MyFormatting<std::string>
{
private:
const std::string& nucleus;
public:
MyFormatting(const std::string& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "std::string:" << w.nucleus;
}
};
// c-string
template <>
class MyFormatting<char*>
{
private:
const char* nucleus;
public:
MyFormatting(const char* n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "c-string:" << w.nucleus;
}
};
MyFormatting<char*> my_formatting(const char* n)
{
return MyFormatting<char*>{n};
}
// std::vector
template <class T>
class MyFormatting<std::vector<T>>
{
private:
const std::vector<T>& nucleus;
public:
MyFormatting(const std::vector<T>& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
os << "std::vector:[";
for (const auto& x : w.nucleus)
{
os << x << " ";
}
os << "]";
return os;
}
};
Next, let's use the wrapper with boost::variant. The constructor of the wrapper enables conversion between variant of nuclues types to variant of the wrappers.
boost::variant<int, std::string> var_int = 50;
boost::variant<int, std::string> var_str = "fifty";
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_int = var_int;
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_str = var_str;
std::cout << var_int << " " << var_str << std::endl;
std::cout << var_fmt_int << " " << var_fmt_str << std::endl;
But boost::variant<MyFormatting<int>, MyFormatting<std::string>> spells too long. We can make it shorter.
template <class... T>
using Variant_Formatting_t = boost::variant < MyFormatting<T>... > ;
std::cout << Variant_Formatting_t<int, std::string>{var_int} << " " << Variant_Formatting_t<int, std::string>{var_str} << std::endl;
Since boost::variant use macro-template metaprogramming to emulate variadic template instead of using C++11 variadic template, I cannot make it cleaner using type deduction. This is the furthest I can get to.