3

Following this question: How to negate a predicate function using operator ! in C++?
I want to create an operator ! can work with any functor that inherited from unary_function. I tried:

template<typename T>
inline std::unary_negate<T> operator !( const T& pred ) {
 return std::not1( pred );
}

The compiler complained:

Error 5 error C2955: 'std::unary_function' : use of class template requires template argument list c:\program files\microsoft visual studio 10.0\vc\include\xfunctional 223 1 Graphic
Error 7 error C2451: conditional expression of type 'std::unary_negate<_Fn1>' is illegal c:\program files\microsoft visual studio 10.0\vc\include\ostream 529 1 Graphic
Error 3 error C2146: syntax error : missing ',' before identifier 'argument_type' c:\program files\microsoft visual studio 10.0\vc\include\xfunctional 222 1 Graphic
Error 4 error C2065: 'argument_type' : undeclared identifier c:\program files\microsoft visual studio 10.0\vc\include\xfunctional 222 1 Graphic
Error 2 error C2039: 'argument_type' : is not a member of 'std::basic_ostream<_Elem,_Traits>::sentry' c:\program files\microsoft visual studio 10.0\vc\include\xfunctional 222 1 Graphic
Error 6 error C2039: 'argument_type' : is not a member of 'std::basic_ostream<_Elem,_Traits>::sentry' c:\program files\microsoft visual studio 10.0\vc\include\xfunctional 230 1 Graphic

Any idea?

Update
Follow "templatetypedef" solution, I got new error:

Error 3 error C2831: 'operator !' cannot have default parameters c:\visual studio 2010 projects\graphic\graphic\main.cpp 39 1 Graphic
Error 2 error C2808: unary 'operator !' has too many formal parameters c:\visual studio 2010 projects\graphic\graphic\main.cpp 39 1 Graphic
Error 4 error C2675: unary '!' : 'is_prime' does not define this operator or a conversion to a type acceptable to the predefined operator c:\visual studio 2010 projects\graphic\graphic\main.cpp 52 1 Graphic

Update 1
Complete code:

#include <iostream>
#include <functional>
#include <utility>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <string>

#include <boost/assign.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/std/map.hpp>
#include <boost/assign/std/set.hpp>
#include <boost/assign/std/list.hpp>
#include <boost/assign/std/stack.hpp>
#include <boost/assign/std/deque.hpp>

struct is_prime : std::unary_function<int, bool> {
 bool operator()( int n ) const {
  if( n < 2 )
   return 0;
  if( n == 2 || n == 3 )
   return 1;
  if( n % 2 == 0 || n % 3 == 0 )
   return 0;
  int upper_bound = std::sqrt( static_cast<double>( n ) );
  for( int pf = 5, step = 2; pf <= upper_bound; ) {
   if( n % pf == 0 )
    return 0;
   pf  += step;
   step = 6 - step;
  }
  return 1;
 }
};

/*
template<typename T>
inline std::unary_negate<T> operator !( const T& pred, typename T::argument_type* dummy = 0 ) {
 return std::not1<T>( pred );
}
*/

inline std::unary_negate<is_prime> operator !( const is_prime& pred ) {
 return std::not1( pred );
}

template<typename T>
inline void print_con( const T& con, const std::string& ms = "", const std::string& sep = ", " ) {
 std::cout << ms << '\n';
 std::copy( con.begin(), con.end(), std::ostream_iterator<typename T::value_type>( std::cout, sep.c_str() ) );
 std::cout << "\n\n";
}

int main() {
 using namespace boost::assign;
 std::vector<int> nums;
 nums += 1, 3, 5, 7, 9;
 nums.erase( remove_if( nums.begin(), nums.end(), !is_prime() ), nums.end() );
 print_con( nums, "After remove all primes" ); 
}

Thanks,
Chan Nguyen

Community
  • 1
  • 1
roxrook
  • 13,511
  • 40
  • 107
  • 156
  • I don't know if the TR1 stuff is any better (I hope so), but boost bind is 100x easier to use for this sort of thing. I'd look there. – Mordachai Jan 04 '11 at 21:09
  • This code isn't causing any problems in g++. Perhaps there's something elsewhere in the code? – templatetypedef Jan 04 '11 at 21:20
  • What happens if you say `return std::not1(pred);` ? – wilhelmtell Jan 04 '11 at 21:21
  • @wilhelmtell: Thanks, but VS gave the same error as described above. – roxrook Jan 04 '11 at 21:26
  • Why are you doing this? Your template is being too greedy and and trying to match every single class type whether or not it's a suitable functor. If a class is relying on a base class for an appropriate `operator!` your template is "stealing" `!` by being a better match. It's simply not a good idea to try and implement `operator!` as a completely general function template. – CB Bailey Jan 04 '11 at 21:31
  • @Charles Bailey: sorry. I really did not think that far. The reason that motivated me to do so is I just want it to be generic. So is there a workaround for this problem? – roxrook Jan 04 '11 at 21:35
  • 1
    @Chan: Yes, don't make overly greedy templates. You can't write an `operator!` for every single type in use in your program so don't try. – CB Bailey Jan 04 '11 at 22:34
  • Thanks wilhelmtell and Charles Bailey, I think I should accept this solution. – roxrook Jan 04 '11 at 23:06

3 Answers3

3

Here’s one way to do this based on the "Substitution Failure is Not an Error" (SFINAE) principle. This states that during overload resolution, if the C++ compiler tries instantiating a template and encounters a problem, it doesn't trigger a compilation error. Instead, it just removes that particular template from consideration. The idea here is that if you overload a function (i.e. have lots of different functions with the same name but different parameters), some of which are templates and some of which aren't, you'll never get a compiler error if one of the candidate template functions makes no sense.

This particular technique can help you out here it two different ways. First, let's suppose right now that I have a black box called "IsAdaptable" that can look at a type and tell me whether or not a particular type is an adaptable function. Given this information, how can we make your operator ! function only apply to types that are adaptable? Well, using the SFINAE principle, we'd need to somehow make the template function signature invalid in the case where the input type isn't adaptable. There are a lot of ways to do this, but one common one uses a helper template called "enable if." Here's the full implementation of an enable-if template:

template <bool Condition, typename T> struct EnableIf {
     typedef T type;
};
template <typename T> struct EnableIf<false, T> {
     // Empty
};

This is a strange template. It's parameterized over two arguments - a boolean condition and a type - in such a way that if the condition is true, EnableIf exports a its second argument as type, and if the condition is false, it doesn't export anything at all.

The utility of the enable if template is that it lets you write functions that are only available if certain properties hold true of the template argument. So suppose, for example, that you want to write a function like this one:

template <typename T>
ReturnType MyFunction(/* ... arguments ... */) {
    /* ... body ... */
}

However, you only want this function to be usable if some predicate "Predicate" holds for the type T. Then you can change the function to look like this:

template <typename T>
typename EnableIf<Predicate<T>::value, ActualReturnType>::type MyFunction(/* ... arguments ... */) {
    /* ... body here ... */
}

The idea here is that if you call the function MyFunction with some type parameter T, one of two cases holds. First, if Predicate<T>::value is true, then the EnableIf ends up exporting a nested type called type that's equivalent to ActualReturnType, and the function works as usual. On the other hand, though, if Predicate<T>::value is false, then the EnableIf instance doesn't have a type type nested inside of it. The compiler detects this, and since "substitution failure is not an error," it removes the function from consideration. If there are other possible overloads still left in consideration, then the compiler will pick some other one of those. If not, then it reports an error, since none of the possible MyFunction functions are still valid.

The idea, in your case, is to write operator ! like this:

template <typename Pred>
typename EnableIf<IsAdaptable<Pred>::value, std::unary_negate<Pred> >::type
operator! (const Pred& p) {
     return std::not1(p);
}

This uses the above trick to say "this operator ! function is available only for types that are adaptable functions." We're now halfway there - given an implementation of IsAdaptable, we're done.

The problem is that writing IsAdaptable is not at all easy. It ends up using a series of hacks so awful that it will make you cry. But fear not! It's not that hard to understand once you see the big picture.

The way that we can make IsAdaptable work is by using SFINAE in an entirely new way. Here's a high-level sketch of the idea. Suppose that we have two functions that are overloads of another, one of which returns a type called "Yes" and one of which returns a type called "No." We then write these functions so that the "Yes" version always takes precedence over the "No" version, but the "Yes" version is only available if some given type is adaptable. In that case, upon calling the function, one of two cases will hold true:

  1. The type in question is adaptable. In that case, both versions of the function are available, but the "Yes" version has priority over the "No" version and so the "Yes" version is called.
  2. The type in question is not adaptable. In that case, the only version of the function that's available is the "No" version, and so that's the one that's called.

But how can you construct functions like this? It turns out there's a clever but not particularly complex solution to this:

template <typename T> Yes TestFunction(typename T::argument_type* argument);
template <typename T> No  TestFunction(...);

Both of these functions are named TestFunction. The first one is parameterized over some type T and takes as an argument a T::argument_type* pointer. The second takes a varargs parameter. Now, if for some type T we try making the following function call:

TestFunction<T>(NULL);

Then the first version of this function is only available if T has a type called argument_type nested inside of it. The second version is always available. Due to the way that C++ overload resolution works, though, a varargs function (a function taking ...) will never be selected over some function that's more specific with its argument list. Consequently, this above expression has type Yes if T has argument_type nested inside it and has type No otherwise. We're almost there - if we can somehow detect what the return type is, we've got a test to see whether T is adaptable!

The way that we'll do this final step is a bit roundabout. The idea is that we'll define types Yes and No so that they have different sizes:

typedef char Yes;
struct No {
    char dummy[32];
};

Now we know that sizeof(Yes) == 1 and sizeof(No) > 1. Pulling all this together gives us the final version of IsAdaptable:

template <typename T> struct IsAdaptable {
private:
    typedef char Yes;
    struct No {
         char dummy[32];
    };

    template <typename U> static Yes test(typename U::argument_type*);
    template <typename U> static No  test(...);

public:
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

This struct contains all of the above functions, and then exports 'true' if test<T>(0) returns a Yes and false otherwise. Notice that because sizeof doesn't actually evaluate its argument (it just tells you how many bytes it uses) we never actually have to implement either of these functions.

Pulling absolutely everything together gives this final solution:

template <bool cond, typename T> struct EnableIf {
  typedef T type;
};
template <typename T> struct EnableIf<false, T> {

};

template <typename T> struct IsAdaptable {
private:
  typedef char Yes;
  struct No {
    char buffer[32];
  };

  template <typename U> static Yes test(typename U::argument_type*);
  template <typename U> static No  test(...);

public:
  static const bool result = (sizeof(test<T>(0)) == sizeof(Yes));
};

template<typename T>                                                                                                                                           
inline typename EnableIf<IsAdaptable<T>::result, std::unary_negate<T> >::type operator !(const T& pred) {                                                      
  return std::not1( pred );                                                                                                                                    
}  

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • @templatetypedef: Great thanks! It worked like a charm ^_^ ! I love your solution, a bit complex to me though. I will ask if I can't figure out how it works, is that ok? – roxrook Jan 04 '11 at 23:40
  • @Chan- Glad it worked out! Would you like me to edit my solution to go into way more depth about how it works? – templatetypedef Jan 04 '11 at 23:52
  • @templatetypedef: That would be more than great ;) ! Thanks a lot for your patience and dedication. – roxrook Jan 04 '11 at 23:54
  • @templatetypedef : amazing! But I really need some times to absorb it. Very detailing ;) – roxrook Jan 05 '11 at 00:39
  • @templatetypedef: Thanks again for your clear explanation. I think I got it now. Invaluable answer ;) – roxrook Jan 05 '11 at 02:58
2

The obscure error you get is a classic example of the C++ template errors. What it's trying to say is that you're trying to call the operator !() function with an incompatible type. std::unary_negate<> expects its template parameter to have an argument_type typedef, and the object you passed to the function doesn't have this typedef.

That said, I'm not sure what you're trying to achieve. The standard library already has this functionality, albeit with a different name: std::not1(). Why do you want to wrap it? Users know exactly what std::not1(f) means, but !f looks to me like a pitfall with respect to what a user might expect in a given context.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • what I want to achieve is the similar design to boost-string algo. All the predicates from this class have an overloading version of operator !. I just feel like the operator ! makes more sense to the users. http://www.boost.org/doc/libs/1_44_0/doc/html/string_algo/quickref.html – roxrook Jan 04 '11 at 21:53
0

I don't know exactly what you are really compiling, but I suspect that you are using std::unary_function without the needed template parameters, isn't it?

Could you show the complete code?

Vicente Botet Escriba
  • 4,305
  • 1
  • 25
  • 39