Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With C++ meta programming, how to figure out whether you are in a class method or static/global function?

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
like image 388
Patrick B. Avatar asked Sep 01 '25 15:09

Patrick B.


1 Answers

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).

like image 73
463035818_is_not_a_number Avatar answered Sep 06 '25 13:09

463035818_is_not_a_number