14

I'm trying to check if an operator exists at compile time, if it doesn't I just want it ignored, is there any way to do that?

example operator:

 template <typename T>
 QDataStream& operator<<(QDataStream& s, const QList<T>& l);
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • http://www.boost.org/doc/libs/1_39_0/libs/type_traits/doc/html/index.html might or might not help you. :) – Kos Dec 13 '10 at 23:45
  • Thanks, didn't really help but gave me an idea on what to search for. – OneOfOne Dec 14 '10 at 05:34
  • @OneOfOne: This is the information Concepts are intending to provide, check http://www.boost.org/doc/libs/1_45_0/libs/concept_check/concept_check.htm to know how to create the meta operator that'll check that (for use with `enable_if`) – Matthieu M. Dec 14 '10 at 07:35
  • 3
    The link you provided in your answer is coming up as an attack page by Firefox. Place any code you wish to display either in the actual post, or use something like codepad.org please don't send people to sites with attack-code javascripts embedded. –  Dec 14 '10 at 08:11
  • @Oxsnarder: If your FireFox flags www.boost.org as suspect, that indicates a problem with your FireFox and/or DNS setup. Boost is a very trustworthy organisation, and you can safely post www.boost.org links here. – MSalters Dec 14 '10 at 09:41
  • 2
    @MSalters: Read the comment VERY carefully. Read the post, I know you guys in the netherlands do a lot of pot and it probably affects your ability to read/think, so I'll let it fly just this time. :) –  Dec 14 '10 at 10:35
  • @Matthieu thanks for the link, I didn't know about it, however I try to avoid boost. Still, a very interesting read. – OneOfOne Dec 14 '10 at 11:32
  • @OneOfOne: I understand :) Let's hope it'll be part of the next C++ standard :) – Matthieu M. Dec 14 '10 at 12:11
  • Yeah hope so too, I just try avoid adding extra dependences if i can help it. – OneOfOne Dec 14 '10 at 12:25

3 Answers3

12

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.

OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • If an operator was already defined, won't it result in ambiguous overloading? – watson1180 Dec 14 '10 at 03:26
  • 1
    No, since it's a template, the "real" operator will be prefered. – OneOfOne Dec 14 '10 at 05:03
  • 1
    What if the "real" operator is also a template? – watson1180 Dec 14 '10 at 05:41
  • 3
    The link you provide is coming up as an attack page by Firefox. –  Dec 14 '10 at 08:10
  • @watson1180 I haven't tried that actually, might cause problems, but so far it's working fine for what I'm using it for. @Oxsnarder I removed the link, it didn't give me any troubles yesterday. – OneOfOne Dec 14 '10 at 11:30
  • 1
    @OneOfOne I had a eureka moment after reading your fallback namespace solution, but it won't work for me. Tested on VS10 and ideone (gcc 4.7.2) ( http://ideone.com/qzjpcL ) – f4. Mar 27 '13 at 18:21
6

This is an old question but it's worth noting that Boost just added this ability for almost all operators with their newest Operator Type Traits. The specific operator OP asked about is tested with boost:has_left_shift.

Cassie Dee
  • 3,145
  • 2
  • 18
  • 11
  • I checked this out, bug this class is riddled with all the same bugs and issues as all the other workarounds I've seen and tried. :( –  Feb 24 '12 at 07:40
  • @AscensionSystems Could you be more specific? – Cassie Dee Feb 24 '12 at 14:11
  • Just look up the documentation on it. It lists a few known issues which basically make it useless because of the nature of the issues. –  Feb 24 '12 at 14:47
  • Those aren't bugs, they are known limitations. And whether or not they "make it useless" depends entirely on what you want to use them for. I personally have found `has_left_shift` to be extremely useful for the specific task I needed it for. Do you know of a better solution for achieving what the OP asked for in C++? – Cassie Dee Feb 24 '12 at 20:03
  • Yeah actually I ended up using cdiggins::any in my application where I needed to detect the << operator support and rather than querying in this way, I wrote a series of template parameters using boost as well as my own custom classes which does some trickery with casting and boost enable_if<>. Objects that do not support the bit left shift get detected at compile time and the generated template code changes functionality to suit each case. –  Feb 25 '12 at 04:57
  • For me the boost has_left_shift didn't work because it failed when checking some STL containers and I'd prefer to use STL in every single application I have or ever will write in C++. :) –  Feb 25 '12 at 04:58
  • Interesting. I didn't have any of these problems. – Cassie Dee Feb 25 '12 at 13:57
2

It isn't too easy and in C++03 it isn't in general possible. If you use int* and int* for op<< for example, you will get a hard error at compile time. So for non-class types, you need to filter out the types that the Standard forbids.

For op+ I once wrote such a thing for the kicks. Note that I'm using C headers, because I needed to test the code with the clang compiler too, which at that time didn't support my C++ headers:

#include <stddef.h>
#include <stdio.h>

namespace detail {
struct any { 
  template<typename T> any(T const&); 
};
struct tag { char c[2]; };

int operator,(detail::tag, detail::tag);
template<typename T> void operator,(detail::tag, T const&);
char operator,(int, detail::tag);
}

namespace fallback {
  detail::tag operator+(detail::any const&, detail::any const&);
}

namespace detail {
template<typename T>
struct is_class {
  typedef char yes[1];
  typedef char no[2];

  template<typename U>
  static yes &check(int U::*);
  template<typename U>
  static no  &check(...);

  static bool const value = sizeof check<T>(0) == 1;
};

template<typename T>
struct is_pointer { typedef T pointee; static bool const value = false; };
template<typename T>
struct is_pointer<T*> { typedef T pointee; static bool const value = true; };

template<typename T, typename U>
struct is_same {
  static bool const value = false;
};

template<typename T>
struct is_same<T, T> {
  static bool const value = true;
};

template<typename T> 
struct is_incomplete_array {
  static bool const value = false;
};

template<typename T>
struct is_incomplete_array<T[]> {
  static bool const value = true;
};

template<typename T>
struct is_reference {
  typedef T referee;
  static bool const value = false;
};

template<typename T>
struct is_reference<T&> {
  typedef T referee;
  static bool const value = true;
};

// is_fn checks whether T is a function type
template<typename T>
struct is_fn {
  typedef char yes[1];
  typedef char no[2];

  template<typename U>
  static no &check(U(*)[1]);

  template<typename U>
  static yes &check(...);

  // T not void, not class-type, not U[], U& and T[] invalid
  // => T is function type
  static bool const value = 
    !is_same<T const volatile, void>::value && 
    !is_class<T>::value && 
    !is_incomplete_array<T>::value &&
    !is_reference<T>::value &&
    (sizeof check<T>(0) == 1);
};

template<typename T, bool = is_fn<T>::value>
struct mod_ty {
  typedef T type;
};

template<typename T>
struct mod_ty<T, true> {
  typedef T *type;
};

template<typename T>
struct mod_ty<T[], false> {
  typedef T *type;
};

template<typename T, size_t N>
struct mod_ty<T[N], false> {
  typedef T *type;
};

// Standard says about built-in +:
//
// For addition, either both operands shall have arithmetic or enumeration type,
// or one operand shall be a pointer to a completely defined object type and 
// the other shall have integral or enumeration type.

template<typename T> struct Ty; // one particular type
struct P; // pointer
struct Nc; // anything nonclass
struct A; // anything
struct Fn; // function pointer

// matches category to type
template<typename C, typename T, 
         bool = is_pointer<T>::value, 
         bool = !is_class<T>::value,
         bool = is_fn<typename is_pointer<T>::pointee>::value>
struct match {
  static bool const value = false;
};

// one particular type
template<typename T, bool P, bool Nc, bool Fn>
struct match<Ty<T const volatile>, T, P, Nc, Fn> {
  static bool const value = false;
};

// pointer
template<typename T, bool F>
struct match<P, T, true, true, F> { 
  static bool const value = true;
};

// anything nonclass
template<typename T, bool P, bool Fn>
struct match<Nc, T, P, true, Fn> {
  static bool const value = true;
};

// anything
template<typename T, bool P, bool Nc, bool Fn>
struct match<A, T, P, Nc, Fn> {
  static bool const value = true;
};

// function pointer
template<typename T>
struct match<Fn, T, true, true, true> {
  static bool const value = true;
};

// one invalid combination
template<typename A, typename B>
struct inv;

// a list of invalid combinations, terminated by B = void
template<typename A, typename B>
struct invs;

// T[] <=> T[N] => T*
// void() => void(*)() 
// T& => T
// trying to find all invalid combinations
// for built-in op+
typedef 
invs<
  inv<Ty<float const volatile>, P>,
invs<
  inv<Ty<double const volatile>, P>,
invs<
  inv<Ty<long double const volatile>, P>,
invs<
  inv<Ty<void * const volatile>, Nc>,
invs<
  inv<Ty<void const* const volatile>, Nc>,
invs<
  inv<Ty<void volatile* const volatile>, Nc>,
invs<
  inv<Ty<void const volatile* const volatile>, Nc>,
invs<
  inv<Fn, Nc>,
invs<
  inv<Ty<void const volatile>, A>,
invs<
  inv<P, P>,
void
> > > > > > > > > > invalid_list;

// match condition: only when ECond<true> is passed by specialization,
// then it will be selected.
template<bool> struct ECond;

template<typename L, typename T, typename U, typename = ECond<true> >
struct found_impl;

// this one will first modify the input types to be plain pointers
// instead of array or function types. 
template<typename L, typename T, typename U>
struct found : found_impl<L, 
                          typename mod_ty<
                            typename is_reference<T>::referee>::type, 
                          typename mod_ty<
                            typename is_reference<U>::referee>::type> 
{ };

// match was found.
template<typename F, typename B, typename R, typename T, typename U>
struct found_impl<invs<inv<F, B>, R>, T, U, 
                  ECond<(match<F, T>::value && match<B, U>::value) ||
                        (match<B, T>::value && match<F, U>::value)> > {
  static bool const value = true;
};

// recurse (notice this is less specialized than the previous specialization)
template<typename H, typename R, typename T, typename U, typename Ec>
struct found_impl< invs<H, R>, T, U, Ec > : found_impl<R, T, U> {
};

// we hit the end and found nothing
template<typename T, typename U, typename Ec>
struct found_impl< void, T, U, Ec > { 
  static bool const value = false;
};

using namespace fallback;

template<typename T, typename U, 
         bool found_invalid = found<invalid_list, T, U>::value>
struct is_addable {
  static T t;
  static U u;
  static bool const value = sizeof (detail::tag(), (t+u), detail::tag()) != 1;
};

template<typename T, typename U>
struct is_addable<T, U, true> {
  static bool const value = false;
};

}

template<typename T, typename U> struct is_addable {
  static bool const value = detail::is_addable<T, U>::value;
};

Of course, it's very important to do tests afterwards

// this one can be added
struct test {
  test operator+(test) { return(*this); }
};

// this one cannot be added
struct nono { };

// this fails because of an ambiguity, because there is a comma
// operator taking a variable parameter on its left hand side.     
struct fails { fails operator+(fails); };

template<typename T>
void operator,(T const&, fails);


int main() {
  printf("%d\n", is_addable<test, test>::value);
  printf("%d\n", is_addable<int, float>::value);
  printf("%d\n", is_addable<nono, nono>::value);
  printf("%d\n", is_addable<int*, int>::value);
  printf("%d\n", is_addable<int[1], int>::value);
  printf("%d\n", is_addable<int[1], float[2]>::value);
  printf("%d\n", is_addable<int*, float*>::value);
  printf("%d\n", is_addable<void*, float>::value);
  printf("%d\n", is_addable<void, int>::value);
  printf("%d\n", is_addable<void(), int>::value);
  printf("%d\n", is_addable<int, void(**)()>::value);
  printf("%d\n", is_addable<float*&, int*&>::value);
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212