I am trying to learn how to use SFINAE.
For practice purposes, I was trying to make an std::ostream wrapper in order to make a custom formatter.
Here is my SFINAE and custom output class.
// Tester
template <class O>
struct is_ostreamable {
template <class T>
static auto check(T t) -> decltype(std::declval<std::ostream &>() << t, std::true_type());
template <class>
static auto check(...) -> std::false_type;
public:
static constexpr bool value{std::is_same_v<decltype(check<O>(0)), std::true_type>};
};
// Custom class
struct CustomOutput {
// Constructor etc...
CustomOutput(std::ostream &os = std::cout) : os{os} {}
std::ostream &os;
// Problematic template function
template <class O, class = std::enable_if_t<is_ostreamable<O>::value>>
CustomOutput &operator<<(O o) {
os << o;
return *this;
}
};
It words perfectly to not enable the template for struct or class that cannot be printed via operator<<.
However, with this SFINAE, ostream manipulators are not working... And I can't figure out why.
The error, and my expectations:
int main(void){
CustomOutput{} << "hi"; // Fine
std::vector<int> vec;
// CustomOutput{} << vec; // Error. Expected
CustomOutput{} << std::endl; // Error. WHY?
}
Maybe I missed something? Any help would be greatly appreciated.
First, fix your ostreamable class. Currently, your class requires T to be copy-constructible from 0. This is not the case for many classes. It should use std::declval to create the value instead:
template <class O>
struct is_ostreamable {
template <class T>
static auto check(int) -> decltype(std::declval<std::ostream &>() << std::declval<T>(), std::true_type());
template <class>
static auto check(...) -> std::false_type;
public:
static constexpr bool value{std::is_same_v<decltype(check<O>(0)), std::true_type>};
};
Two changes are made here:
The operand to decltype uses std::declval<T>() to create the object of type T. std::declval<T> is an (intentionally undefined) function template that generates an object of type T when used in an unevaluated operand (such as that to decltype, or sizeof, noexcept operator, etc.) without dependence on a specific construction signature (copy-construction from 0 in your case).
The parameter to check is replaced with int. The initializer of the value variable calls check with the argument 0, so this int parameter ensures that (int) ranks higher than (...) in overload resolution, so that the true_type overload gets chosen when possible.
You need to provide a special overload for function-style manipulators (std::endl, std::flush, etc.):
using manip = std::ostream& (*)(std::ostream&);
CustomOutput& operator<<(manip m) {
os << m;
return *this;
}
There is, unfortunately, no way to make the generic template version support this feature. This is because std::endl is a function template:
template <class CharT, class Traits>
std::basic_ostream<CharT, Traits>& endl(td::basic_ostream<CharT, Traits>& os);
For a function template to be used, the appropriate template arguments have to be determined. It is not possible to deduce the type-template parameter T as a generic template.
Anyway, this is probably the only special overload you are going to need.
I know there is already an accepted answer but I would like to mention a bit prettier C++20 way of doing the same thing by using concepts:
#include <iostream>
#include <concepts>
using OManipulator= std::ostream&(&)(std::ostream &);
template <typename T>
concept OStreamable = requires(T t) {
std::declval<std::ostream&>() << t;
};
struct CustomOutput {
std::ostream &os;
CustomOutput(std::ostream &os = std::cout)
: os{os}
{}
template <typename T> requires OStreamable<T>
CustomOutput& operator<<(T out) {
os << out;
return *this;
}
CustomOutput& operator<<(OManipulator out) {
os << out;
return *this;
}
};
int main(void){
CustomOutput{} << "hello";
CustomOutput{} << std::endl;
CustomOutput{} << "world";
}
Basically, in C++20, problem with manipulators needs to be solved in the same way as pre-C++20, by providing a special overload for them.
Related
I am trying to define a has_ostream_operator<T> SFINAE test for checking whether I can cout a given type. I have it working, but only if in my definition of has_ostream_operator I call operator<< as a method rather than as an infix operator. In other words this works:
decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>
This does not:
decltype(std::declval<std::ostream>() << std::declval<T>())>
Test case below (can also see at http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9). Note that I included a definition of void_t since I'm only on C++14.
#include <iostream>
namespace std {
template<class...>
using void_t = void;
}
template<class, class = std::void_t<>>
struct has_ostream_operator : std::false_type {};
template<class T>
struct has_ostream_operator<
T,
std::void_t<
decltype(
std::declval<std::ostream>().operator<<(std::declval<T>()))>>
: std::true_type {};
struct Foo {};
template<class X>
void print(
const X& x,
std::enable_if_t<has_ostream_operator<X>::value>* = 0)
{
std::cout << x;
}
template<class X>
void print(
const X&,
std::enable_if_t<!has_ostream_operator<X>::value>* = 0)
{
std::cout << "(no ostream operator<< implementation)";
}
int main()
{
print(3); // works fine
print(Foo()); // this errors when using infix operator version
return 0;
}
I'm assuming your "infix" version used this expression:
std::declval<std::ostream>() << std::declval<T>()
The reason that matches for Foo is because the first part, declval<ostream>() produces an rvalue of type ostream&&. This matches a non-member operator<<:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
That overload simply forwards the call along:
Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to os << value).
You should instead check for that directly. All the overloads take an ostream by lvalue reference, so you should test that too:
std::declval<std::ostream&>() << std::declval<T>()
You need
std::declval<std::ostream&>() << std::declval<T>()
// ^
std::declval<std::ostream>() is an rvalue; you are hitting the catch-all operator<< overload for rvalue streams.
If you use infix notation, the rvalue stream inserter is found, since declval returns rvalues per se; [ostream.rvalue]:
template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);
This overload currently accepts all arguments for x. I've submitted LWG #2534, which, if resolved accordingly, will make your initial code work as expected.
A temporary workaround is to make declval return an lvalue reference, i.e. adjust the template argument to one:
std::declval<std::ostream&>() << std::declval<T>()
The subject is already touched in this boost-variant-ambiguous-construction question.
But my issue is not with types convertible to each other, but with completely unrelated types.
Simplified example:
// types not related in any way
class A {};
class B {};
class C {};
class D {};
using ABC_variant = boost::variant<A,B,C>;
using D_optional = boost::optional<D>;
The issue was related with the fact that optional on some type was not printable. But, completely unrelated output operator for some variant was trying to accept this boost::optional type (D_optional).
See:
std::ostream& operator << (std::ostream& os, const ABC_variant&)
{
return os << "ABC";
}
int main() {
D_optional dopt;
std::cout << dopt;
}
You can see on ideone - plenty of compilers errors saying that it does not know what do you want to print bool or ABC_variant and in case of ABC_variant it does not know how to convert D_optional to ABC_variant. As I understand that boost::optional is convertible to bool and first alternative is correct I have no idea why it tries to use ABC_variant conversion...
Furthermore I simplified this example even more and give up with boost::optional:
int main() {
D d;
std::cout << d;
}
Now, it does not have "bool alternative" and just complaining that it tries to construct ABC_variant from D:
prog.cpp:23:15: required from here
/usr/include/boost/variant/variant.hpp:1591:38: error: no matching
function for call to 'boost::variant::initializer::initialize(void*, D&)'
initializer::initialize(
This here is ostream operator for ABC_variant.
Of course I know that writing ostream operator for D/D_opt will solve the issue - but the problem is with diagnostic: if boost::variant did not accept any type as argument for its constructor, the compiler would tell me simple true - not this bunch of misleading sentences...
I doubt it is in such way by design - maybe there is some fixes ongoing?
Fortunately I and ideone uses the same compiler and boost: gcc4.9 and boost1.58.
I have created boost ticket for this problem. Just to clarify what is the real problem:
Real problem is with this boost.variant "converting" constructor accepting any type - there is no restriction, even so natural like that argument type should be convertible to any of this boost.variant instantiation types:
template <typename T>
variant(const T& operand)
{
convert_construct(operand, 1L);
}
My proposed solution can be something like this:
template <typename T, typename ...C>
struct IsAnyOf;
template <typename T, typename ...C>
struct IsAnyOf<T,T,C...> : std::true_type {};
template <typename T>
struct IsAnyOf<T> : std::false_type {};
template <typename T, typename C1, typename ...C>
struct IsAnyOf<T,C1,C...> : IsAnyOf<T, C...> {};
template <typename T,
typename EnableIf = typename std::enable_if<IsAnyOf<VariantType...>::value>::type>
variant(const T& operand)
But for now - the only solution is not to create any non template function accepting instantiation of boost::variant. So either create function template or aggregate instantiation of boost::variant in some struct type.
So either this:
template <typename T>
typename std::enable_if<std::is_same<T,ABC_variant>::value, std::ostream&>::type
operator << (std::ostream& os, const T&)
{
return os << "ABC";
}
Or something like this:
struct ABC_variant
{
boost::variant<A,B,C> v;
ABC_variant(const A&);
ABC_variant(const B&);
ABC_variant(const C&);
};
The issue is fixed in boots.1.62
Recently I came over an issue with clang++ 5.0.0 compiler where via ADL it was not picking up the correct function on Mac (but g++ did it correctly on Linux). I would like to know whether its a compiler issue OR poor class design in general.
Here is an example code (purely for illustration purpose):
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <template<typename> class E, typename T>
Ops& operator<< (Ops& op, const E<T>& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
int main() {
test::Ops op;
int k = 9;
test::Any<int> a;
op << a;
return 0;
}
I would like to know how ADL and template argument deduction wouldwork in step wise manner to find the best match ?
Would there be any situation for the same 'main body' the member function would be preferred in place of the free function ? (This is what is happening in the product build)
Thanks in advance.
This is what happens in detail and what every compiler should do: a candidate template function is found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)
while the second candidate is generated via ADL using template argument deduction (cfr. temp.deduct.conv)
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
Afterwards overload resolution kicks in (cfr. 13.3.3) and the non-member one (F1) is preferred to the member (F2) one since
F1 and F2 are function template specializations, and the function template for F1 is more specialized
than the template for F2 according to the partial ordering rules described in 14.5.6.2.
and thus selected as the function to be called.
To answer your question: it depends on the overload resolution rules. Being a member function or in an inner scope doesn't affect the result and something like
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <typename E>
Ops& operator<< (Ops& op, const E& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
would just trigger an overload resolution error 'use of overloaded operator '<<' is ambiguous'.
As a plus: the member function is wrong even if it were chosen: this->val is assigned a non-integer type.
These two candidate functions are in the overload set:
// member function template, found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)
// non-member function template, found by ADL
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
In operator lookup, no preference is given to members versus non-members. After template argument substitution, both function template specializations exactly match (with qualification conversions) the supplied argument types. But the function taking E<T> is more specialized than the one taking T, so the non-member function is chosen for this reason.
Apple clang 5.0.0 is based on LLVM clang 3.3svn. I can't find any version of LLVM clang which selects the member function. It could be a bug in Apple's code, but IMHO it's more likely to be some subtle difference in the code you are actually compiling or your environment. Have you tried compiling your example code with the suspect compiler?
Consider these two functions:
template <class Type,
class = typename std::enable_if</*HAS OPERATOR <<*/>::type>
void f(std::ostream& stream, const Type& value);
template <class Type,
class... DummyTypes,
class = typename std::enable_if<sizeof...(DummyTypes) == 0>::type>
void f(std::ostream& stream, const Type& value, DummyTypes...);
As the non-variadic overload has the priority over the variadic overload, I want to check whether the type has the operator<< with an std::ostream using std::enable_if in the first version.
So what should I write instead of /*HAS OPERATOR <<*/ ?
The following should work
template <class Type,
class = decltype(std::declval<std::ostream&>() << std::declval<Type>())>
void f(std::ostream& stream, const Type& value)
{
stream << value;
}
(note you don't need to use std::enable_if in this case)
Using trailing return type (1), you can actually have a foretaste of concepts:
template <typename Type>
auto f(std::ostream& out, Type const& t) -> decltype(out << t, void()) {
// ...
}
Because of SFINAE, this overload can only be selected if the type of out << t can be resolved, and this implies that an overload of << exists that accepts both parameters.
The one pitfall is that this does not work if you need the contrary, that is enabling a function if this overload does not exists. In this case an enable_if strategy (and the symmetric disable_if) is necessary, as far as I know.
(1) thanks to Simple for helping out with the syntax
It is easiest to check when you have the arguments around, i.e., I would rather try to use something like this:
template <typename Type>
auto f(std::ostream& out, Type const& value)
-> typename std::enable_if<sizeof(out << value) != 0>::type {
...
}
A similar effect could be obtained using std::declval() but off-hand I'm not sure about creating references.
If I have
template<class T>
TalkyBuffer& operator<<(T const &object) { // Template
...
}
TalkyBuffer& operator<<(TalkySerialisable const &object); // Override
and a class
class A : public TalkySerialisable {
...}
Then if I perform
TalkyBuffer b;
A test;
b << test;
Then gcc is calling the Template function rather than the Override function
However if I specifically define an override
TalkyBuffer& operator<<(A const &object); // Override without polymorphism
Then gcc picks that one.
Is there a practical way to override a templated function with an abstract class?
I read this but it doesn't shed light onto what happens when you throw polymorphism into the mix:
http://www.gotw.ca/publications/mill17.htm
Also I couldn't find a solution here but perhaps I'm using the wrong terms.
When defining the function TalkyBuffer& operator<<(TalkySerialisable const &object); You are not overriding. You are overloading the tmeplated function.
But, when the complier sees b << test;, it searches for an operator that wants an A. It has one, it's the templated function that requires no automatic cast. This is the best choice.
The overloaded function requires an automatic cast (from A to TalkySerialisable) on the parameters to fit the declaration, and is not the best choice.
I think it's possible to use a simple function based solution, reusing function overload for derivation.
struct specialized {};
struct generic {};
template <class T>
TalkyBuffer& serialize(TalkyBuffer& buffer, T const& object, generic) {
...
}
generic dispatch(...) {} // always picked up last in overload resolution
template <class T>
TalkyBuffer& TalkyBuffer::operator<<(T const& object) { // Template
return serialize(*this, object, dispatch(object));
}
Now, let's implement your custom class:
TalkyBuffer& serialize(TalkyBuffer& buffer,
TalkySerialisable const& object,
specialized);
specialized dispatch(TalkySerialisable const&) {}
And create a derived one:
class A: public TalkySerialisable {};
So, what happens ?
TalkyBuffer::operator<<(T const&) will be picked up
when trying to resolve the overload for serialize, it will first compute the result of dispatch
when resolving the result of dispatch, dispatch(TalkySerializable const&) is a better match than dispath(...), thus the return type is specialized
the generic serialize cannot be used (there is no conversion from specialized to generic), so inheritance kicks in
A solution using Boost.enable_if:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>
template<typename T>
typename boost::disable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object) { // Template for non TalkySerializable
...
}
template <typename T>
typename boost::enable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object); // Template overload for TalkySerializable
...
TalkyBuffer b;
A test;
b << test; // calls operator<< <A>(A const &), which instantiates
// the overload for TalkySerializable
b << 41; // calls operator<< <int>(int const &), which corresponds to
// the "default" overload
I'm not sure this is the best solution, but I failed to find a better one: specializing the template does not work either.
As #Matthieu noted in the comment, the previous solution has the major drawback that the base template needs to know that it will be overloaded, which is an unnecessary coupling that hinders extensibility.
To solve this problem, I came up with a new approach using tag dispatching, along with trait classes and compile-time introspection using Boost.MPL macros.
// TalkyBuffer.hpp
#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>
// defines a metafunction has_talky_buffer_tag<T> that allows us to know at
// compile-time if T has a member type named talky_buffer_tag
BOOST_MPL_HAS_XXX_TRAIT_DEF(talky_buffer_tag)
// tag for the default case
struct default_talky_buffer_tag {};
// trait class for obtaining the tag of a type
template <typename T, typename Enable = void >
struct talky_buffer_trait
{
typedef default_talky_buffer_tag type;
};
// specialization for types that provide a nested typedef
template <typename T>
struct talky_buffer_trait<T,
typename boost::enable_if<has_talky_buffer_tag<T> >::type>
{
typedef typename T::talky_buffer_tag type;
};
struct TalkyBuffer
{
// Insertion operator, which calls an implementation function that can
// be overloaded depending on the tag
template<typename T>
TalkyBuffer & operator<<(T const & object)
{
typename talky_buffer_trait<T>::type tag;
return insertionOperatorImpl(*this, object, tag);
}
};
// default implementation
template <typename T>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, T const & object,
default_talky_buffer_tag)
{
std::cout << "default";
return buf;
}
//-------
// TalkySerializable.hpp
struct TalkySerializable
{
struct tag {};
typedef tag talky_buffer_tag;
};
// A inherits from the nested typedef
struct A : public TalkySerializable {};
// implementation for TalkySerializable objects
template <typename Serializable>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, Serializable const & object,
TalkySerializable::tag)
{
std::cout << "specialized";
return buf;
}
//-------
int main()
{
TalkyBuffer b;
A test;
b << test; // outputs "specialized"
b << 41; // outputs "default"
}
To provide new implementations of the insertion operator for a given type T, one needs to provide a new type to act as a tag (TypeSerializable::tag in our example), provides a way to associate T with the new tag (either by using a nested typedef as in the example, or by specializing the trait class: template <> talky_buffer_trait<T> { typedef new_tag type };), and finally overload the implementation function (insertionOperatorImpl in the example).