c++如何在成员函数中有条件地传递this指针

ruarlubt  于 2023-01-15  发布在  其他
关注(0)|答案(4)|浏览(145)

当a)在c自由函数中或b)在类的成员函数中时:
我如何将a)nullptr分别B)this 传递给一个对两者都有效的通用宏?
我知道,我们不应该使用宏--但是我有遗留代码。也许以后宏可以被替换。但是我认为问题仍然是一样的:如何检测a)在c
的自由函数中或者B)在类的成员函数中,并且因此使用a)nullptr或者b)this ptr进行处理?

#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;
}

例如,应在MYMACRO内检测到空指针或 * 此 * 指针。

附录1

下面的一些评论问的目的-在混凝土从@Nicol Bolas。
想象一下MYMACRO已经有一些功能,但是应该扩展以额外提供一个新的日志函数(可选)。当MYMACRO用于成员函数时,类名和这个ptr值将被记录用于跟踪和关联。只是作为一个例子。那么MYMACRO需要知道是否在类成员函数的上下文中使用(即,是否有一些 * 这个 * ptr可用)。
一般性谈话:是关于“反思”

klsxnrf1

klsxnrf11#

如果您不介意为需要调用宏的任何类继承接口类,则可以采用以下方法:

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);
}

注意,我将使用“MYMACRO”作为对执行该工作的程序部分的引用。* 它显然不是宏!* 相反,它只是一个函数,将使用NULL指针调用DoSomething
为了在类中重载此行为,必须定义一个接口:

class SomethingDoer
{
protected:
    void MYMACRO(SomeVar x)
    {
        DoSomething(this, x);
    }
};

然后,任何需要调用它的类都应该继承这个接口,当该类的方法调用MYMACRO时,它将使用SomethingDoer::MYMACRO而不是另一个函数。

class SomeClass : public SomethingDoer
{
public:
    bool MemberFunc();
};

bool SomeClass::MemberFunc()
{ 
    SomeVar x;
    MYMACRO(x);
    return false;
}

如果你的宏还做了其他更方便使用宏的事情,那也没关系,你仍然可以用宏来实现,但是让与“this”相关的事情使用我描述过的机制。
如果您确实需要this的类型是正确的,那么可以做一个小的调整:

template <class T>
class SomethingDoer
{
protected:
    void MYMACRO(SomeVar x)
    {
        DoSomething((T*)this, x);
    }
};

class SomeClass : public SomethingDoer<SomeClass>
{
public:
    bool MemberFunc();
};
flvlnr44

flvlnr442#

对于独立函数来说,没有this,所以这部分问题没有意义。如果你的意思是如何检查局部变量x是否有效,那就没有意义了。它总是有效的,并且在它的方法中,这不会是nullptr,除非你的代码做了一些可怕的事情(例如,不知何故从其他线程中断了堆栈)。
宏定义只是简单地替换代码,因此如果您可以编写

bool SomeClass::MemberFunc(void)
{ 
  if(!this) return;
  doSomething();
}

你可以写

#define CHECK_THIS  if(!this) return
bool SomeClass::MemberFunc(void)
{ 
  CHECK_THIS;
  doSomething();
}

宏展开发生在编译之前,预处理器不分析你的代码,所以你不能改变宏的内容。也就是说,它必须发生在代码中。
一种“肮脏”的方法是定义一些全局函数,在每个对象中都有局部对应函数。在这种情况下,任何不包含该定义的对象方法都将表现为没有this
它可以通过继承来完成,就像paddy在他的答案中所展示的,或者类体中的另一个宏:

//  global MYMACRO for free fuctions.
void MYMACRO ()  {};

#define DECLARE_MYMACRO \
 void MYMACRO ()  { \
    if(!this) doSomething(); \
 } 

class MyClass {
   DECLARE_MYMACRO
public:
   void memberFunc(void);
};
  • 作为一项规则,检查this是否为nullptr是一种偏执和无用的努力 *。您可以尝试仅用于调试目的,但您不能使用它来创建错误的“坚固”代码。事情是调用成员函数,并使用指向类类型示例的空指针是未定义的行为。编译器可以自由地假设未定义的行为没有发生。因此,在实践中,编译器可能会抛出那个if,因为它在定义的行为边界内不可能是nullptr。这不是假设,流行的编译器在优化打开时会这么做。

检查对象的地址是否为nullptr必须在第一次在特定范围内使用它的成员之前进行,即在成员之外。在第一次使用或调用之后,检查没有意义。

ukxgm1gy

ukxgm1gy3#

对于c++17来说,这只完成了一半的工作
这种方法不起作用,因为__PRETTY_FUNCTION__也有一个嵌套在名称空间中的自由函数的':'。(参见下面Nicol Bolas的注解-谢谢Nicol!)

#include <string_view>
#include <iostream>

constexpr bool ContainsColon(std::string_view str)
{
    return str.find(':') != str.npos;
}

#define MYMACRO                                                                                             \
{                                                                                                           \
    if constexpr(ContainsColon(__PRETTY_FUNCTION__)) {                                                      \
        constexpr void *ptr = nullptr; /* still todo */                                                     \
        std::cout << "MYMACRO used in a member function of class this ptr = " << (long)ptr << 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;
}

namespace TestSpace {

void SomeOtherFreeFunction(void)
{
    MYMACRO;
}

} // namespace TestSpace

int main(int, char**)
{
    MyStruct myStruct;
    myStruct.SomeMemberFunction();
    SomeFreeFunction();
    // ERROR! - this also reports to be called from a member function :-(
    TestSpace::SomeOtherFreeFunction();
    return 0;
}

输出:

MYMACRO used in a member function of class this ptr = 0
MYMACRO used in a free function
MYMACRO used in a member function of class this ptr = 0 # WRONG HERE!
s4chpxco

s4chpxco4#

我认为你最好做这样的事情:

#include <type_traits>
#include <iostream>

#define DECLARE_GETTHIS() auto get_this() {return this;}
#define MYMACRO()  log_this(get_this())

void *get_this() {return nullptr;}

template<typename T>
void log_this(T *_this) {
    if constexpr (std::is_void_v<T>) {
        std::cout << "We are in freestanding function" << std::endl;
    } else {
        std::cout << "We are in class function, this: " << _this << std::endl;
    }
}

struct S {
    DECLARE_GETTHIS();
    void func() {
        MYMACRO();
    }
};

int main() {
    MYMACRO();
    S s;
    s.func();
}

当然,要使它工作,你需要在所有的基类中插入DECLARE_GETTHIS();,但是我没有看到任何其他的方法没有 * 一些 * 修改类。
全局get_this()的声明可以放置在使用inline说明符定义MYMACRO()的同一标头中。
此外,如果需要在旧版本中使用if constexpr,可以将constexpr删除。

相关问题