method compile time assertion; still not working

2019-04-13 23:03发布

问题:

I need a easy way to assert inside a template that a template parameter implements a method (or one of its parent classes). I've read Concept check library but is hard to find an easy example to do simple checks like this one.

I've tried to follow other posts (like this one and this other one), which i've modified so i can make it generic for many method types (in my example Foo (methodName) and has_foo (Checker name) will, once working correctly, be wrapped as macro arguments so it can be used for any method)

the code i have for the moment is this one:

template <typename TypeToBeChecked, typename Sign>
class has_foo { 
   static_assert( false , "inside root declaration of " "has_foo" );
public: 
   static const bool result = false;
}; 

template <typename TypeToBeChecked , typename R> 
class has_foo < TypeToBeChecked , R(void) > 
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(void)" ); 
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> class Helper{};
   template <typename U> static no deduce(U*, Helper<R (BaseMixin::*)(), &U::Foo>* = 0);
   static yes deduce(...);
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

template <typename TypeToBeChecked , typename R , typename ARG1> 
class has_foo< TypeToBeChecked , R(ARG1) >
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(ARG1)" ); 
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(ARG1){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> class Helper{};
   template <typename U> 
   static no deduce(U*, Helper<R (BaseMixin::*)(ARG1), &U::Foo>* = 0);
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

template <typename TypeToBeChecked , typename R , typename ARG1 , typename ARG2>
class has_foo< TypeToBeChecked , R(ARG1, ARG2) >
{ 
   static_assert( false , "inside specialization of " "has_foo" " for R(ARG1 , ARG2)" );
   class yes { char m;};
   class no { yes m[2];};
   struct BaseMixin { R Foo(ARG1,ARG2){} };
   struct Base : public TypeToBeChecked, public BaseMixin {};
   template <typename T, T t> 
   class Helper{};
   template <typename U> 
   static no deduce(U*, Helper<R (BaseMixin::*)(ARG1,ARG2), &U::Foo>* = 0); 
   static yes deduce(...);
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};

template< typename Type >
struct Connector
{  
   static_assert( has_foo< Type , int(int, double) >::result , "Type has no Foo method" );
   void Operate() {
      Type t;
      t.Foo(3);
   }
};

struct Bla1 { int Foo(double f) { return (int)f; } };
struct Bla2 { int Foo(int g, double h) { return g+(int)h;} };

int main() 
{
   //Connector< Bla1 > a;
   Connector< Bla2 > b;
};

When i compile this example code (g++ 4.4.3 ubuntu with -std=c++0x option so the static_assert is recognized) i get this:

$ g++ test.cpp -std=c++0x -o test
test.cpp:72: error: static assertion failed: "inside root declaration of has_foo"
test.cpp:79: error: static assertion failed: "inside specialization of has_foo for R(void)"
test.cpp:93: error: static assertion failed: "inside specialization of has_foo for R(ARG1)"
test.cpp:108: error: static assertion failed: "inside specialization of has_foo for R(ARG1 , ARG2)"

wait just right there, (notice that Connector< Bla1 > a is commented) my first question is:

1) am i right to assume that if the assertion is being evaluated, the containing template is being instantiated?

EDIT: answered by GMan: the static_assert is evaluated during parsing, and not when the template is instantiated. Replacing false with sizeof(TypeToBeChecked)==0 makes it bound to compile-time

2) am i right to assume that since the static assert inside Connector template class is instantiating has_foo with int(int, double) signature, then the single-parameter and parameterless specializations should NOT be instantiated? whats wrong with my assumptions?

EDIT: this assumption is right but now that i fixed according to 1) answer, the instantiation process is now behaving as expected

3) if i uncomment the Connector< Bla1 > a line, i would expect it to fail (since Bla1 only has a Foo with a single parameter signature. However it is not. Any idea what might be wrong? specially taking in consideration the first linked post

回答1:

Taking into account also the comments on the answer in the first linked question (this one), your template checks if there is a member Foo, but it does not check the signature of that member.

To check the signature, you need code like this (checks for operator() that can be invoked with specified arguments; reproduced from this usenet post on comp.lang.c++.moderated by Roman.Perepelitsa@gmail.com):

template <typename Type>
class has_member
{
   class yes { char m;};
   class no { yes m[2];};

   struct BaseMixin
   {
     void operator()(){}
   };

   struct Base : public Type, public BaseMixin {};

   template <typename T, T t>  class Helper{};

   template <typename U>
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>*
= 0);
   static yes deduce(...);

public:
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)
(0)));

};

namespace details
{
   template <typename type>
   class void_exp_result
   {};

   template <typename type, typename U>
   U const& operator,(U const&, void_exp_result<type>);

   template <typename type, typename U>
   U& operator,(U&, void_exp_result<type>);

   template <typename src_type, typename dest_type>
   struct clone_constness
   {
     typedef dest_type type;
   };

   template <typename src_type, typename dest_type>
   struct clone_constness<const src_type, dest_type>
   {
     typedef const dest_type type;
   };

}

template <typename type, typename call_details>
struct is_call_possible
{
private:
   class yes {};
   class no { yes m[2]; };

   struct derived : public type
   {
     using type::operator();
     no operator()(...) const;
   };

   typedef typename details::clone_constness<type, derived>::type
derived_type;

   template <typename T, typename due_type>
   struct return_value_check
   {
     static yes deduce(due_type);
     static no deduce(...);
     static no deduce(no);
     static no deduce(details::void_exp_result<type>);
   };

   template <typename T>
   struct return_value_check<T, void>
   {
     static yes deduce(...);
     static no deduce(no);
   };

   template <bool has, typename F>
   struct impl
   {
     static const bool value = false;
   };

   template <typename arg1, typename r>
   struct impl<true, r(arg1)>
   {
     static const bool value =
       sizeof(
            return_value_check<type, r>::deduce(
             (((derived_type*)0)->operator()(*(arg1*)0),
details::void_exp_result<type>())
                         )
            ) == sizeof(yes);

   };

   // specializations of impl for 2 args, 3 args,..
public:
   static const bool value = impl<has_member<type>::result,
call_details>::value;

};

Usage example:

struct Foo
{
   void operator()(double) const {}
   void operator()(std::string) const {}

};

int main()
{
   STATIC_ASSERT((is_call_possible<Foo, void(double)>::value));
   STATIC_ASSERT((is_call_possible<Foo, void(int)>::value));
   STATIC_ASSERT((is_call_possible<Foo, void(const char *)>::value));
   STATIC_ASSERT((!is_call_possible<Foo, void(void *)>::value));

}