Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect existence of private member

I want to write a type trait to check if some type has a member member. If member were public, there are any number of ways to do this (e.g. void_t), the most concise of which is probably Yakk's can_apply (which could eventually be called std::is_detected):

struct C {     int member; };  template <typename T> using member_type = decltype(&T::member);  template <typename T> using has_member = can_apply<member_type, T>;  static_assert(has_member<C>{}, "!"); // OK 

But if the member were private, this trait fails, since the access on member is ill-formed (we are not friends) and there is no differentiation between ill-formed due to access reasons and ill-formed due to this-thing-doesn't-exist reasons:

class D {     int member; };  static_assert(has_member<D>{}, "!"); // error 

Is there a way to write such a member checker across all access controls?

like image 375
Barry Avatar asked Sep 14 '15 13:09

Barry


People also ask

What is private member function in C?

Private: The class members declared as private can be accessed only by the member functions inside the class. They are not allowed to be accessed directly by any object or function outside the class. Only the member functions or the friend functions are allowed to access the private data members of the class.

Can a member function be private?

Private Member function in C++ We can place these functions in the private section. A private member function can only be called by another function that is a member of its class. Even an object can not invoke a private function using the dot operator.

What is private member function explain how it is useful?

A function declared inside the class's private section is known as "private member function". A private member function is accessible through the only public member function. (Read more: data members and member functions in C++).


2 Answers

There is indeed a way for non-final non-union class types:

namespace detail {     struct P {typedef int member;};     template <typename U>     struct test_for_member : U, P     {         template <typename T=test_for_member, typename = typename T::member>         static std::false_type test(int);         static std::true_type test(float);     }; } template <typename T> using test_for_member =   std::integral_constant<bool, decltype(detail::test_for_member<T>::test(0)){}>; 

Demo. The trick is to check whether lookup into different base classes will yield an ambiguity. [class.member.lookup]/2:

Member name lookup determines the meaning of a name (id-expression) in a class scope (3.3.7). Name lookup can result in an ambiguity, in which case the program is ill-formed. […] Name lookup takes place before access control (3.4, Clause 11).

Note that GCCs lookup is broken insofar as it ignores non-type names for lookup in typename-specifiers.

like image 94
Columbo Avatar answered Oct 03 '22 11:10

Columbo


You can create another class MemberBase that does have that member, and then subclass the two classes (the class to check T and BaseMember) and try to access the member of the subclass. If T also has a member member, then you will get an ambiguity problem.

Code:

#include <type_traits>  // Yakk's can_apply  template<class...>struct voider{using type=void;}; template<class...Ts>using void_t=typename voider<Ts...>::type;  template<class...>struct types{using type=types;}; namespace details {   template<template<class...>class Z, class types, class=void>   struct can_apply : std::false_type {};   template<template<class...>class Z, class...Ts>   struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >:     std::true_type   {}; } template<template<class...>class Z, class...Ts> using can_apply = details::can_apply<Z,types<Ts...>>;  // Main code  class MemberBase {     public:         int member; };  template<class ToCheck> class MemberCheck: public ToCheck, public MemberBase { };  template <typename T> using member_type = decltype(&T::member);  template <typename T> using hasnot_member = can_apply<member_type, MemberCheck<T>>;  template <typename T>  using static_not = std::integral_constant<bool, !T::value>;  template <typename T> using has_member = static_not<hasnot_member<T>>;  // Tests  class A {     int member; };  class Ap {     public:     int member; };  class B {     float member; };  class C {     int member(); };  class D { };  static_assert(has_member<A>{}, "!"); // ok static_assert(has_member<Ap>{}, "!"); // ok static_assert(has_member<B>{}, "!"); // ok static_assert(has_member<C>{}, "!"); // ok static_assert(has_member<D>{}, "!"); // fail 

However, this definitely smells like a dirty hack to me.

like image 27
Petr Avatar answered Oct 03 '22 11:10

Petr