0

C++11 adds lots of new class templates which allow to test type traits statically, i.e. detect problems at compile time. I'm writing a test for a class and I need to make sure a given method is public.

The "dynamic" solution is to create an object and call the method, then the compiler will complain if it doesn't work. But it's possible a different kind of error occurs, and it will make the resulting error message more confusing. If possible, it would be best to test method access level using static assertion.

Is it possible or I really have to create an object for that?

(also, what do I do if I need the method to be private/protected)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524

2 Answers2

2

This compiles for me:

#include <type_traits>

namespace has_foo_imp
{

template <class T>
auto
test(T&& t) -> decltype(t.foo(), std::true_type());

auto
test(...) -> std::false_type;

}  // has_foo_imp

template <class T>
struct has_foo
    : public std::integral_constant<bool,
                        decltype(has_foo_imp::test(std::declval<T>()))::value>
{
};

class A
{
    void foo();
public:
};

class B
{
public:
    void foo();
};

int
main()
{
    static_assert(!has_foo<A>::value, "");
    static_assert( has_foo<B>::value, "");
}
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Why not directly inherit `public decltype(has_foo_imp::test(...))`? – Xeo Feb 18 '13 at 15:46
  • This actually compiles on Clang 3.2, but not on GCC 4.7.2. I'm wondering who's right and why... – Andy Prowl Feb 18 '13 at 15:53
  • @AndyProwl [Clang](http://stackoverflow.com/questions/8984013/can-sfinae-detect-private-access-violations) I think. – Yakk - Adam Nevraumont Feb 18 '13 at 15:58
  • @Yakk: Seems correct. It all depends on how you interpret "immediate context", but I would say in this case the error does indeed occur in the immediate context of the function type. – Andy Prowl Feb 18 '13 at 16:05
2

As far as I can tell, the following is standards compliant:

#include <type_traits>

template<typename T,typename=void>
struct NullaryFooCanBeCalled:std::false_type {};

template<typename T>
struct NullaryFooCanBeCalled<
      T,
      typename std::enable_if<
         std::is_same<
               decltype(std::declval<T>().Foo()),
               decltype(std::declval<T>().Foo())
         >::value >::type
      >:
   std::true_type {};



struct PrivateFoo {private:void Foo() {}};
struct PublicFoo {public:void Foo() {}};
struct ProtectedFoo {protected:void Foo() {}};
struct StrangeFoo {
   struct Bar { void operator()() {}; };
   Bar Foo;
};

#include <iostream>

int main() {
   std::cout << "PrivateFoo:" << NullaryFooCanBeCalled<PrivateFoo>::value << "\n";
   std::cout << "PublicFoo:" << NullaryFooCanBeCalled<PublicFoo>::value << "\n";
   std::cout << "ProtectedFoo:" << NullaryFooCanBeCalled<ProtectedFoo>::value << "\n";
   std::cout << "StrangeFoo:" << NullaryFooCanBeCalled<StrangeFoo>::value << "\n";
}

On the other hand, compiler support is poor for this language quirk.

Clang 3.2 compiles and works. gcc 4.7.2 fails to build. Intel 13.0.1 compiles but returns the wrong values (true in every case!)

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • This looks complicated, I don't understand how it works. If compiler support is poor (I use GCC) I probably shouldn't use it, but anyway - can you please explain in words how it works? I mean, what is the language principle it relies on? – cfa45ca55111016ee9269f0a52e771 Feb 18 '13 at 18:39
  • SFINAE -- basically, I test if `t.Foo()` is well formed, and if so, the template specialization with `::value = true` holds, and otherwise `::value = false` holds. The problem is that the this particular substitution failure that is not an error is treated as an error in some compilers. On the other hand, the standardese is sufficiently complex that I could be wrong, and maybe it should be an error! – Yakk - Adam Nevraumont Feb 18 '13 at 18:44
  • Then I guess there's no easy way... if some compilers support it while others don't, it's best to wait until it's supported better (or C++ gets a better way to check access levels, e.g. new class templates which allow to directly check method access level easily like e.g. std::is_constructible) – cfa45ca55111016ee9269f0a52e771 Feb 18 '13 at 18:51