Skip to content

Instantly share code, notes, and snippets.

@maddouri
Last active January 5, 2024 01:38
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save maddouri/0da889b331d910f35e05ba3b7b9d869b to your computer and use it in GitHub Desktop.
Save maddouri/0da889b331d910f35e05ba3b7b9d869b to your computer and use it in GitHub Desktop.
Checking the Existence of a C++ Class Member at Compile Time
// A compile-time method for checking the existence of a class member
// @see https://general-purpose.io/2017/03/10/checking-the-existence-of-a-cpp-class-member-at-compile-time/
// This code uses "decltype" which, according to http://en.cppreference.com/w/cpp/compiler_support
// should be supported by Clang 2.9+, GCC 4.3+ and MSVC 2010+ (if you have an older compiler, please upgrade :)
// As of "constexpr", if not supported by your compiler, you could try "const"
// or use the value as an inner enum value e.g. enum { value = ... }
// check "test_has_member.cpp" for a usage example
/// Defines a "has_member_member_name" class template
///
/// This template can be used to check if its "T" argument
/// has a data or function member called "member_name"
#define define_has_member(member_name) \
template <typename T> \
class has_member_##member_name \
{ \
typedef char yes_type; \
typedef long no_type; \
template <typename U> static yes_type test(decltype(&U::member_name)); \
template <typename U> static no_type test(...); \
public: \
static constexpr bool value = sizeof(test<T>(0)) == sizeof(yes_type); \
}
/// Shorthand for testing if "class_" has a member called "member_name"
///
/// @note "define_has_member(member_name)" must be used
/// before calling "has_member(class_, member_name)"
#define has_member(class_, member_name) has_member_##member_name<class_>::value
// usage example: check for the existence of a "sayHi" member //////////////////////////////////////
// clang++ -std=c++11 -pedantic -Wall -Wextra test_has_member.cpp -o test_has_member && ./test_has_member
// g++ -std=c++11 -pedantic -Wall -Wextra test_has_member.cpp -o test_has_member && ./test_has_member
#include <iostream> // cout, endl
#include <iomanip> // std::boolalpha
#include "has_member.hpp"
struct A // has a "sayHi" member
{
void sayHi() { std::cout << "Hi there!" << std::endl; }
};
struct B // doesn't have a "sayHi" member
{
void sayBye() { std::cout << "Bye bye!" << std::endl; }
};
// define a "sayHi" "member checker" class
define_has_member(sayHi);
int main()
{
using std::cout;
using std::endl;
cout << std::boolalpha; // display "true" or "false" for booleans
A a;
B b;
// check the existence of "sayHi"
cout << "has_member(A, sayHi) " << has_member(A, sayHi) << endl;
cout << "has_member(B, sayHi) " << has_member(B, sayHi) << endl;
cout << endl;
// same thing, using decltype on instances
cout << "has_member(decltype(a), sayHi) " << has_member(decltype(a), sayHi) << endl;
cout << "has_member(decltype(b), sayHi) " << has_member(decltype(b), sayHi) << endl;
return 0;
}
@837951602
Copy link

Is it possible that define_has_member can be called for multiple times before usage?

@maddouri
Copy link
Author

maddouri commented Aug 12, 2019

Is it possible that define_has_member can be called for multiple times before usage?

If you are thinking about calling define_has_member with the same member name multiple times in the same compilation unit, then that's unfortunately not possible because it would re-define the "checker" class (see http://coliru.stacked-crooked.com/a/704e9ea3732a26c2)

@JesseMaurais
Copy link

If you modify the template to take a dummy int and fill it with the COUNTER macro, rather than pasting ##member_name, it should allow you to use the same name multiple times in the translation unit.

@gopalrander
Copy link

Is there a way to verify existence of private members too in c++?

@minglu10
Copy link

minglu10 commented Jan 5, 2024

Is it possible to check if A has a function sayHi(int)? basically check signature also.

@JesseMaurais
Copy link

Is it possible to check if A has a function sayHi(int)? basically check signature also.

I'm assuming you mean a way to disambiguate sayHi(float) from sayHi(int) or sayHi(string)

I believe this will only work if the signature is hinted at via a template (i.e. a function template on the member sayHi<T>(T)) because C++ gives no way to specify the overload except at the call site and the way yes_type is defined here is not going to do that. Perhaps there is a more convoluted way of defining yes_type to use std::declval so that the disambiguation can happen. At a first glance, it is not obvious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment