When being a) in a c free function or b) in a class' member function:
How can i pass a) nullptr respecively b) the this to a generic macro that should work for both?
I know, we shouldn't use macros - but i have legacy code. Maybe later the macro can be replaced. But i think the question remains the same: How to detect if a) in c free function or b) in a class' member function and as a consequence use a) nullptr or b) this ptr for processing?
#include <iostream>
#define MYMACRO \
{ \
if (1) { /* what to use here? */ \
std::cout << "MYMACRO used in a member function of class this ptr = " << "" /* print this here */ << std::endl; \
} \
else { \
std::cout << "MYMACRO used in a free function" << std::endl; \
} \
}
struct MyStruct
{
MyStruct() = default;
~MyStruct() = default;
void SomeMemberFunction(void)
{
MYMACRO;
}
};
void SomeFreeFunction(void)
{
MYMACRO;
}
int main(int, char**)
{
MyStruct myStruct;
myStruct.SomeMemberFunction();
SomeFreeFunction();
return 0;
}
i.e. nullptr or this ptr should be detected within the MYMACRO.
CodePudding user response:
If you don't mind inheriting an interface class for any class that needs to invoke your macro, you can take the following approach:
template <class T>
void DoSomething(T* ptr, SomeVar x)
{
std::cout << "Doing something with ptr=" << ptr << "\n";
}
void MYMACRO(SomeVar x)
{
DoSomething<void>(nullptr, x);
}
Note that I will use "MYMACRO" simply as a reference to the part of your program that does the work. It is obviously NOT a macro! Instead, it's just a function that will invoke DoSomething
with a NULL pointer.
In order to overload this behavior in classes, you must define an interface:
class SomethingDoer
{
protected:
void MYMACRO(SomeVar x)
{
DoSomething(this, x);
}
};
And then, any class that needs to invoke it should inherit this interface. When a method of that class calls MYMACRO
, it will use SomethingDoer::MYMACRO
instead of the other function.
class SomeClass : public SomethingDoer
{
public:
bool MemberFunc();
};
bool SomeClass::MemberFunc()
{
SomeVar x;
MYMACRO(x);
return false;
}
If your macro does other stuff as well that is more convenient to use macros for, that's fine. You can still implement as a macro, but have the "this"-related stuff use the mechanism I described.
And if you really need the type of this
to be correct, a small adjustment is:
template <class T>
class SomethingDoer
{
protected:
void MYMACRO(SomeVar x)
{
DoSomething((T*)this, x);
}
};
class SomeClass : public SomethingDoer<SomeClass>
{
public:
bool MemberFunc();
};
CodePudding user response:
For free-standing function there is no this
, so that part of question doesn't make sense. If you're meaning how to check if local variable x
is valid, that's pointless. It's always valid and within its methods this would not be nullptr unless your code boke something. horribly (e.g. somehow broke stack from other thread).
Macro definitions simply replace code, so if you can write
bool SomeClass::MemberFunc(void)
{
if(!this) return;
doSomething();
}
you can write
#define CHECK_THIS if(!this) return
bool SomeClass::MemberFunc(void)
{
CHECK_THIS;
doSomething();
}
Macro unwrapping happens before compilation, preprocessor doesn't analyze your code, so you cannot change macro content contextually. I.e. it must happen in code.
One "dirty" way is to define some global function which would have local counterpart in every object. In that case any method of object that doesn't contain that definition, would behave as there is no this
.
It can be done by inheritance, like paddy had shown in his answer, or another macro in class body:
// global MYMACRO for free fuctions.
void MYMACRO () {};
#define DECLARE_MYMACRO \
void MYMACRO () { \
if(!this) doSomething(); \
}
class MyClass {
DECLARE_MYMACRO
public:
void memberFunc(void);
};
Is as a rule, check if this
is nullptr
is a paranoid AND useless effort. You may try that for debugging purposes only, but you cannot use that to create falsely "rugged" code. Thing is a call to member function with null pointer to class-type instance is Undefined Behaviour. The compiler is free to assume that Undefined Behavior didn't happen. So, on practice, a compiler may just throw that if
out, because this cannot be nullptr
within defined behaviour borders. That's not hypothetical, popular compilers do that when optimization is on.
The check if the address of object is nullptr
must happen before the very first use of its member in particular scope, i.e. outside of member. After first use or call gloves are off, the check makes no sense.