How to detect whether there is a specific PRIVATE member variable in class?
Asked Answered
B

2

4

This question is based on this post.

Goal: I would like to know if a class has the member variable x. I would like to receive true regardless whether or not this variable is private, public or protected.

Approach: You can get the information if a class has a member variable using the following code:


template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Use it with

if constexpr (HasX<my_class>::value) {
   // do stuff with x
} else {
   // ...
}

The above code does not work in this case

struct my_class {
private:
   int x;
};

How can I make this work? I would like HasX<my_class>::value to be true.

Ideas:

Use a friend class which has access to T::x. This does not seem to work. Check out this live example.

Blindworm answered 30/9, 2020 at 14:17 Comment(12)
If the member is private, then it cannot be used from outside the class, so how would the code with the if contexpr take advantage of knowing that info?Kaliski
I would like to use my_class::x from a class which is friend of my_class.Blindworm
@Enrico actually, any private/protected member can be accessed via template specialization. Though I don't think it's possible to test for the existence of something inaccessibleAnabasis
If you can modify the class to add a friend you can as well inspect the definition to see if the member exists ;)Beery
@idclev463035818 I get what you mean. Let's say this member function is templated and I receive an object of type T. Then I do not know if T::x exists.Blindworm
@User12547645, so is the if constexpr inside my_class's friend class?Kaliski
there is a trick, if T has a method template, this is a backdoor to access whatever privates of T. There are other ways to access privates, but beware. Many "solutions" you find (even here on SO) are not valid C++, they make use of some UB and are mainly based on luckBeery
though to just check existance you need no access (i believe). But wait, if T made your class (the one that wants to acces x) a friend then your HasX can work with a modifcation, the acces has to happen in the class that is declared as friend.Beery
I don't quite understand @idclev463035818. Could you give an example?Blindworm
will take some time before I can give it a try, but i got hooked up, will come here laterBeery
Thank you very much @idclev463035818. Also please have a look at my solution. Just in case it needs improvement ;)Blindworm
my ideas were all variations of either of the two answers you already got. If the aim is to have a template that is declared as friend of A, ie it can access As private x but needs to know whether that exists, then perhaps making the trait a friend of A is the simplest. Actually thats your solution (and one of the answers)Beery
M
6

Well... not sure about correctness and limits of this solution... but...

If you define an helper struct with an x element accessible

struct check_x_helper
 { int x; };

you can write a template struct that inherit from both check_x_helper and the class you want to see if contain a x member

template <typename T>
struct check_x : public T, check_x_helper

Inside check_x you can declare (declare only: are used inside a decltype()) as follows

template <typename U = check_x, typename = decltype(U::x)>
static constexpr std::false_type check (int);

static constexpr std::true_type check (long);

Observe the first one, the template one: when the checked class (T) contains an x member, the decltype(U::x) is ambiguous because x is inherited from both T and check_x_helper, so this function is SFINAE discarded.

On contrary, when T doesn't contains an x member, there isn't an ambiguity, the decltype(U::x) is the type of check_x_helper::x (int) and the first check() function remain enabled.

Now you need something as

using type = decltype(check(0));

static constexpr auto value = type::value;

to call check(0) (the int parameter express the preference to the template version) and save the detected value in a static constexpr variable.

The following is a full compiling example

#include <iostream>
#include <utility>

class foo
 { int x; };

struct bar
 { };

struct check_x_helper
 { int x; };

template <typename T>
struct check_x : public T, check_x_helper
 {
   template <typename U = check_x, typename = decltype(U::x)>
   static constexpr std::false_type check (int);

   static constexpr std::true_type check (long);

   using type = decltype(check(0));

   static constexpr auto value = type::value;
 };

int main()
 {
   std::cout << check_x<foo>::value << std::endl;
   std::cout << check_x<bar>::value << std::endl;
 }

Drawback of this solution: decltype(U::x) fail (ambiguity) also when T declare x as a method or as a using type. So given

class foo
 { int x () { return 0;} ; };

or

class foo
 { using x = int; };

from check_x<foo>::value you obtain 1.

Mudskipper answered 30/9, 2020 at 15:3 Comment(0)
B
1

The following seems to work. Please tell me if it needs improvement. Live example.

class Haser {
public:
template <typename T, typename = int>
static constexpr bool HasX = false;

template <typename T> 
static constexpr bool HasX<T, decltype((void) T::x, 0)> = true;
};

struct A { 
private:    
    int x;
    friend Haser;
};

Haser::HasX<A> is true.

Blindworm answered 30/9, 2020 at 14:44 Comment(1)
Yeah, but what's the point? You've made the Haser class a friend of A, and it obviously can see the private members now. But this is possible just because you have the source code of A, otherwise you couldn't do that. It'd be good if you could explain, in the question, why you need this. Maybe someone else would learn something too (beside the mostly obvious thing that friends can see privates).Kaliski

© 2022 - 2024 — McMap. All rights reserved.