Using Sfinae to Check for Global Operator<<

Using SFINAE to check for global operator?

I should have simply been more faithful to this answer.
A working implementation is:

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> {
};

I believe that it does not actually rely on SFINAE.

Is it possible to use SFINAE/templates to check if an operator exists?

I ended up using a fallback namespace :

namespace operators_fallback {
template <typename T>
inline QDataStream& operator<<(QDataStream& s, const T &) { return s; }

template <typename T>
inline QDataStream& operator>>(QDataStream& s, T &) { return s; }

template <typename T>
inline QDebug operator<<(QDebug d, const T &) { return d; }
};

...
inline void load(QDataStream & s) {
using namespace operator_fallback;
s >> item;
}

Also found the proper way to check for operators at compile time (although I'm going with the fallback namespace).

more or less based on this :

namespace private_impl {
typedef char yes;
typedef char (&no)[2];

struct anyx { template <class T> anyx(const T &); };

no operator << (const anyx &, const anyx &);
no operator >> (const anyx &, const anyx &);

template <class T> yes check(T const&);
no check(no);

template <typename StreamType, typename T>
struct has_loading_support {
static StreamType & stream;
static T & x;
static const bool value = sizeof(check(stream >> x)) == sizeof(yes);
};

template <typename StreamType, typename T>
struct has_saving_support {
static StreamType & stream;
static T & x;
static const bool value = sizeof(check(stream << x)) == sizeof(yes);
};

template <typename StreamType, typename T>
struct has_stream_operators {
static const bool can_load = has_loading_support<StreamType, T>::value;
static const bool can_save = has_saving_support<StreamType, T>::value;
static const bool value = can_load && can_save;
};
}
template<typename T>
struct supports_qdatastream : private_impl::has_stream_operators<QDataStream, T> {};

template<typename T>
struct can_load : private_impl::has_loading_support<QDataStream, T> {};

template<typename T>
struct can_save : private_impl::has_saving_support<QDataStream, T> {};

template<typename T>
struct can_debug : private_impl::has_saving_support<QDebug, T> {};

//edit changed has_stream_operators a bit.

//edit removed the link, apparently the site has some attack javascript.

How to check whether operator== exists?

C++03

The following trick works and it can be used for all such operators:

namespace CHECK
{
class No { bool b[2]; };
template<typename T, typename Arg> No operator== (const T&, const Arg&);

bool Check (...);
No& Check (const No&);

template <typename T, typename Arg = T>
struct EqualExists
{
enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) };
};
}

Usage:

CHECK::EqualExists<A>::value;

The 2nd template typename Arg is useful for some special cases like A::operator==(short), where it's not similar to class itself. In such cases the usage is:

CHECK::EqualExists<A, short>::value
// ^^^^^ argument of `operator==`

Demo.



C++11

We need not use sizeof and null reference trick when we have decltype and std::declval

namespace CHECK
{
struct No {};
template<typename T, typename Arg> No operator== (const T&, const Arg&);

template<typename T, typename Arg = T>
struct EqualExists
{
enum { value = !std::is_same<decltype(std::declval<T>() < std::declval<Arg>()), No>::value };
};
}

Demo

Detect operator support with decltype/SFINAE

You need to make your less_than_test function a template, since SFINAE stands for Substitution Failure Is Not An Error and there's no template function that can fail selection in your code.

template <class T>
struct supports_less_than
{
template <class U>
static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
{ }

static std::array<char, 2> less_than_test(...) { }

static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};

int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}

Check if a class has a member function of a given signature

I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
template<typename U, size_t (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
// We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
ReportMemUsage(m,
std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}

Templated check for the existence of a class member function?

Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:

#include <iostream>

struct Hello
{
int helloworld() { return 0; }
};

struct Generic {};

// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };

template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);

public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}

I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.

Operator in namespace scope hiding another in global scope

No that is not a bug. There are three parallel sets of operators considered. Members, non-member operators, and builtins.

The non-member ones are looked up by normal unqualified+ADL lookup, ignoring all class member functions. Hence the global operator is hidden by a lexical more closer one (and an intervening member function wouldn't have hidden other non-members).

Note that overload resolution takes place after name lookup1; in your case the name operator++ was found, but no appropriate overload.

If Bar had been declared globally, and/or the other operator in namespace asdf, ADL (in the former case) or ordinary unqualified lookup (in the latter case) would have dragged the operator in.


1: Overload resolution (...) takes place after name lookup has succeeded. (C++ Standard)

Testing for the presence of the left shift operator

The reason why it does not work is that C++ has sometimes surprising (but well motivated) rules for resolving a function call. In particular, name lookup is first performed in namespace where the call happens and in the namespace of the arguments (for UDTs): if a function with a matching name (or if a matching built-in operator) is found, it is selected (or if more than one are found, overload resolution is performed).

Only if no function with the matching name is found in the namespace of the arguments, the parent namespaces are checked. If a function with a matching name is found is but not viable for resolving the call, or if the call is ambiguous, the compiler will not keep looking up in parent namespaces with the hope of finding a better or unambiguous match: rather, it will conclude that there is no way to resolve the call.

This mechanism is nicely explained in this presentation by Stephan T. Lavavej and in this old article by Herb Sutter.

In your case, the function which checks for the existence of this operator is in the boost namespace. Your arguments are either from the std namespace (ostream, string, vector) or are PODs (int). In the std namespace, a non-viable overload of operator << exist, so the compiler does not bother looking up in the parent (global) namespace, where your overload is defined. It will simply conclude that the (simulated) call done in the boost namespace to check whether operator << is defined can't be resolved.

Now boost::has_left_shift is likely to have some SFINAE machinery for turning what would otherwise be a compilation error into a failed substitution, and will assign false to the value static variable.

UPDATE:

The original part of the answer explained why this does not work. Let's now see if there is a way to work around it. Since ADL is used and the std namespace contains a non-viable overload of operator <<, de facto blocking the attempt to resolve the call, one could be tempted to move the viable overloads of operator << from the global namespace into the std namespace.

Alas, extending the std namespace (and this is what we would do if we were to add new overloads of operator <<) is forbidden by the C++ Standard. What is allowed instead is to specialize a template function defined in the std namespace (unless stated otherwise); however, this doesn't help us here, because there is no template whose parameters can be specialized by vector<int>. Moreover, function templates cannot be partially specialized, which would make things much more unwieldy.

There is one last possibility though: add the overload to the namespace where the call resolution occurs. This is somewhere inside the machinery of Boost.TypeTraits. In particular, what we are interested in is the name of the namespace in which the call is made.

In the current version of the library, that happens to be boost::detail::has_left_shift_impl, but I am not sure how portable this is across different Boost versions.

However, if you really need a workaround, you can declare your operators in that namespace:

namespace boost 
{
namespace detail
{
namespace has_left_shift_impl
{
ostream& operator<<(ostream& stream, const Point& p)
{
stream << p.getStr();
return stream;
}

template <typename T>
std::ostream& operator<<(std::ostream& stream, const std::vector<T>& v)
{
stream << "[";
for(auto it = v.begin(); it != v.end(); ++it)
{
if(it != v.begin())
stream << ", ";
stream << *it;
}
stream << "]";
return stream;
}
}
}
}

And things will start working.

There is one caveat though: while this compiles and runs fine with GCC 4.7.2 with the expected output. However, Clang 3.2 seems to require the overloads to be defined in the boost::details::has_left_shift_impl before the has_left_shift.hpp header is included. I believe this is a bug.



Related Topics



Leave a reply



Submit