How to Detect Whether There Is a Specific Member Variable in Class

How to detect whether there is a specific member variable in class?

Another way is this one, which relies on SFINAE for expressions too. If the name lookup results in ambiguity, the compiler will reject the template

template<typename T> struct HasX { 
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };

template<typename C, C> struct ChT;

template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];

static bool const value = sizeof(f<Derived>(0)) == 2;
};

struct A { int x; };
struct B { int X; };

int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}

It's based on a brilliant idea of someone on usenet.

Note: HasX checks for any data or function member called x, with arbitrary type. The sole purpose of introducing the member name is to have a possible ambiguity for member-name lookup - the type of the member isn't important.

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

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.

How to detect whether there is actually a specific member variable in class?

The problem is that HasX only checks if the name x exists. The ... gets selected if &C::x is ambiguous (which happens if it matches both in Fallback and T). The ChT<> overload gets selected only if &C::x is exactly Fallback::x. At no point are we actually checking the type of T::x - so we never actually check if x is a variable or function or whatever.

The solution is: use C++11 and just check that &T::x is a member object pointer:

template <class T, class = void>
struct HasX
: std::false_type
{ };

template <class T>
struct HasX<T,
std::enable_if_t<
std::is_member_object_pointer<decltype(&T::x)>::value>
>
: std::true_type { };

If &T::x doesn't exist, substitution failure and we fallback to the primary template and get false_type. If &T::x exists but is an overloaded name, substitution failure. If &T::x exists but is a non-overloaded function, substitution failure on enable_if_t<false>. SFINAE for the win.

That works for all of these types:

struct A { 
void x()
{
}

void x(int)
{
}
};

struct B { int X; };
struct C { int x; };
struct D { char x; };

int main() {
static_assert(!HasX<A>::value, "!");
static_assert(!HasX<B>::value, "!");
static_assert(HasX<C>::value, "!");
static_assert(HasX<D>::value, "!");
}

C++17 how to test class has a member variable?

If you are stuck in c++17, there is some infrastructure that you can add to make detection like this much easier.

Detection-idiom

The most reusable/consistent way to detect features like this is via the Detection idiom, which leverages SFINAE through std::void_t in a template.

This can be taken verbatim from std::experimental::is_detected's page from cppreference. This effectively offers C++17 the ability to detect features in a way that is similiar to C++20's concepts; and the infrastructure can be reused easily for just about any detection.

The basics of what you would need are:

#include <type_traits>

namespace detail {
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector {
using value_t = std::false_type;
using type = Default;
};

template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};

} // namespace detail

struct nonesuch{};

template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

Note: The above infrastructure can be reused for any detection. It is a very useful reusable tool in C++17.

With is_detected, all you need is a detector, which is simply a template alias that evaluates to a decltype expression of something that may, or may not, exist.

So in your case, to conditionally detect the presence of T::extra, you can do this with a simple detector like:

template <typename T>
using detect_extra = decltype(T::extra);

Putting it all together now, you can use this detector to conditionally toggle the branch:

if constexpr (is_detected<detect_extra,T>::value) {
// Only do code if 'T' has 'T::extra' (e.g. 'S')
} else {
// Only do code if 'T' does not have 'T::extra' (e.g. 'V')
}

Live Example


If equivalent conversion to a specific type is important, such as extra needing to be convertible to int, you can also use is_detected_convertible and use the detector to check for if the result can be convertible to the desired type. Using the same cppreference page again, you can define is_detected_convertible as:

template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template <class To, template<class...> class Op, class... Args>
using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;

Which allows the check to instead be:

if constexpr (is_detected_convertible<int, detect_extra, T>::value) {
// Only do code if 'T' has 'T::extra' convertible to int (e.g. 'S')
} else {
// Only do code if 'T' does not have 'T::extra', or is not int
}

Live Example

Concepts (C++20+ only)

If you have access to c++20 and beyond, concepts make this much simpler -- since you can simply use a concept + requires clause like:

#include <concepts> // std::same_as

template <typename T>
concept HasExtra = requires(T) {
{T::extra} -> std::same_as<int>;
};

if constexpr (HasExtra<T>) {
// Only do code if 'T' has 'T::extra' and is 'int' (e.g. 'S')
} else {
// Only do code if 'T' does not have 'T::extra' (e.g. 'V')
}

Live Example

How to check if a member name (variable or function) exists in a class, with or without specifying type?

C++03

#define HasMember(NAME) \
template<class Class, typename Type = void> \
struct HasMember_##NAME \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}; \
template<class Class> \
struct HasMember_##NAME<Class, void> \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}

Usage: Simply invoke the macro with whatever member you want to find:

HasMember(Foo);  // Creates a SFINAE `class HasMember_Foo`
HasMember(i); // Creates a SFINAE `class HasMember_i`

Now we can utilize HasMember_X to check X in ANY class as below:

#include<iostream>
struct S
{
void Foo () const {}
// void Foo () {} // If uncommented then type should be mentioned in `HasMember_Foo`
int i;
};
int main ()
{
std::cout << HasMember_Foo<S, void (S::*) () const>::value << "\n";
std::cout << HasMember_Foo<S>::value << "\n";
std::cout << HasMember_i<S, int (S::*)>::value << "\n";
std::cout << HasMember_i<S>::value << "\n";
}

Catches:

  1. In case of methods, if we don't mention the type then the class
    must not have overloaded methods. If it has then this trick fails.
    i.e. even though the named member is present more than once, the result will be false.
  2. If the member is part of base class, then this trick fails; e.g. if B is base of S & void B::Bar () is present, then HasMember_Bar<S, void (B::*)()>::value or HasMember_Bar<S, void (S::*)()>::value or HasMember_Bar<S>::value will give false

Check if a member variable exists in an Object from superClass?

One solution would be to have a method in the superclass called getProperty which can be called from any instance once the presence of the property is verified. In the superclass, the method can return null or throw an exception; override it in the subclass to return the actual property. Like so:

class SuperClass
{
protected boolean hasProperty;

protected Property getProperty()
{
throw new UnsupportedOperationException();
}
}

class SubOne extends SuperClass
{
protected Property prop;

SubOne()
{
this.hasProperty=true;
this.prop=new Property();
}

@Override
protected Property getProperty()
{
return prop;
}
}

class SubTwo extends SuperClass
{
SubTwo()
{
this.hasProperty=false;
}
}

And the code for the iteration would be as follows:

if (superObj.hasProperty)
System.out.println(superObj.getProperty().something);
else
System.out.println("Something");

The advantage of this over a template method is that it works for any application of the properties without needing to be changed.

Templated check for the existence of a class member function?

Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:

#include <iostream>

struct Hello
{
int helloworld() { return 0; }
};

struct Generic {};

// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };

template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);

public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}

I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.

Check if a class has a member function of a given signature

I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
template<typename U, size_t (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
// We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
ReportMemUsage(m,
std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}


Related Topics



Leave a reply



Submit