Is there a way to figure out whether the current scope is a global function/static class-method or a class-method?
I thought of the existence of this
, but I'm unable to find a technique which doesn't error out during compilation when this does not exist.
I'd like to have one function-prototype which I can use in a member-method or global function, but it does behave differently. Without adding something to my classes and without adding additional arguments to my function call.
An example:
class NormalClass
{
public:
void foo(int a, bool b)
{
DEBUG("print from class %d %d\n", a, b);
}
};
void normal_function()
{
DEBUG("print from a normal function");
}
Printing out
Class: print from class 1, 2
Global: print from a normal function
You can use std::source_location
to get a string representing the function name. As far as I know this string is implementation defined though. With gcc it can be easily distinguished if the current scope is a free function, member or static member:
#include <source_location>
#include <iostream>
void check_member_or_static(std::source_location location =
std::source_location::current())
{
std::cout << location.function_name() << "\n";
}
void free() {
check_member_or_static();
}
struct foo {
void member() {
check_member_or_static();
}
static void statfun() {
check_member_or_static();
}
};
int main()
{
free();
foo{}.member();
foo::statfun();
}
Output:
void free()
void foo::member()
static void foo::statfun()
PS: Only after writing this I realize that it looks like you want something at compile time. The above works at runtime only. Moreover, as pointed out in comments, foo::member
does not distinguish a free function called member
in namespace foo
from the member function.
If you are either inside some base class or not the situation is much simpler. You can attach some key to the base. This key can be for example a type definition.
Credits goes to Raymond Chen from whom I stole the trait to check for completeness. The full article is found here.
#include <iostream>
#include <type_traits>
template<typename, typename = void>
constexpr bool is_type_complete_v = false;
template<typename T>
constexpr bool is_type_complete_v
<T, std::void_t<decltype(sizeof(T))>> = true;
#define inside_base is_type_complete_v<struct foobar>
struct base {
private:
struct foobar {};
};
void free() {
std::cout << inside_base;
}
struct foo : base {
void member() {
std::cout << inside_base;
}
static void statfun() {
std::cout << inside_base;
}
};
int main()
{
free();
foo{}.member();
foo::statfun();
}
Output:
011
When namelookup finds a foobar
that is complete the macro evaluates to true
, otherwise false. The actual definition can be private
. A macro is used to introdue the forward declaration in case foobar
is not in scope. The weak point is that the name has to be chosen such that it cannot be looked up elsewhere (which unfortunately has a similar smell as using macros but the effect is only local).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With