如何构造C++使用关键字'this'用于非静态和静态的宏?

How to construct C++ macro that uses 'this' keyword for use in non-statics AND statics?

本文关键字:静态 何构造 用于 关键字 C++ this      更新时间:2023-10-16

我有一个日志宏,我想扩展它以包含调用它的对象的实例的名称。

我已经为所有要继承的对象引入了基类,例如:

class ObjectInstance
{
public:
  ObjectInstance( string name );
  virtual string GetName() const = 0;
}

宏看起来像这样:

#define LOG( type, msg ) LogHandler( type, msg, dynamic_cast<ObjectInstance*>( this ) )

使用LogHandler函数:

void LogHandler( string type,
                 string msg,
                 ObjectInstance* instance )
{
  string instanceName = "Unknown";
  if( instance != NULL )
  {
    instanceName = instance->GetName();
  }
  const string output = type + " | " + msg + " | " + instanceName;
  // Do logging stuff...
}

这似乎是计划出来,但我很快意识到它不会编译时,宏从静态上下文中调用…我不知道该怎么做,到目前为止我还没有在网上找到太多信息。

非静态上下文示例:

class SomeObject : public ObjectInstance
{
  SomeObject( const string& name )
  :
    m_name( name )
  {
    LOG( Info, "Created SomeObject" )
  }
  virtual string GetName() const { return m_name; }
}
SomeObject* ob = new SomeObject( "SomeObject123" );

期望输出:Info | Created SomeObject | SomeObject123

静态上下文示例-这当然不会编译:

class SomeStaticObject
{
  static void DoSomething()
  {
    LOG( Info, "Doing something..." )
  }
}
SomeStaticObject::DoSomething();

期望输出:Info | Doing something…|

我真的希望在静态和非静态上下文中都坚持使用一个宏,因为它已经在大型代码库中广泛使用。

我已经为所有继承

的对象引入了基类

哇!到此为止。这是c++,不是脚本语言。

为项目中的每个类添加公共基类限制了灵活性,将所有内容与日志记录器紧密耦合,并将您推向ObjectInstance集合的滑坡,这会破坏所有类型安全。

有一个更好的方法:

首先,定义一个标签类:
template<class T>
struct name_tag
{};

让LogHandler成为一个模板函数:

template<class T>
void LogHandler( string type,
                 string msg,
                 T* instance )
{
    // get the name by deferring to instance_name(name_tag<>);
    .... << instance_name(name_tag<std::decay_t<T>>()) << ...
}

现在我们只需要为传递给LogHandler的任何类型编写一个instance_name。

// this is your object
struct MyThing {};
// this is its name_generator.
const char* instance_name(name_tag<MyThing>)
{
  return "MyThing";
}

请注意,每个名称生成器的返回类型可以是您想要的任何类型,只要它是可流的。

完整示例,包括可选内容-printer:

#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>
#define LOG(type, msg, obj) LogHandler(type, msg, std::addressof(obj))
template<class T>
struct name_tag
{};
template<class T> const char* instance_name(name_tag<T>) { return typeid(T).name(); }
template<class T> const char* object_contents(const T*) { return "{}"; }
template<class T>
void LogHandler( std::string type,
                std::string msg,
                T* instance )
{
    std::clog << "type: " << type
    << ", message: " << std::quoted(msg)
    << " : object type: " << instance_name(name_tag<std::decay_t<T>>())
    << " " << object_contents(instance)
    << std::endl;
}
// this is your object
struct MyThing {
    int a = 4;
    double b = 6.6;
};
// this is its name_generator.
const char* instance_name(name_tag<MyThing>)
{
    return "MyThing";
}
// this is a contents printer.
std::string object_contents(const MyThing* p)
{
    std::ostringstream ss;
    ss << "{ a: " << p->a
    << ", b: " << p->b
    << " }";
    return ss.str();
}

int main()
{
    MyThing t;
    LOG("whatever goes here", "hello", t);
}
预期输出:

type: whatever goes here, message: "hello" : object type: MyThing { a: 4, b: 6.6 }