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

时间:2016-09-02 12:44:07

标签: c++ static macros

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

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

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

预期输出:信息|创建SomeObject | SomeObject123

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

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

SomeStaticObject::DoSomething();

预期输出:信息|做某事...... |

我真的很想在静态和非静态上下文中只使用一个宏,因为它已经在大型代码库中广泛使用。

1 个答案:

答案 0 :(得分:2)

  

我为所有要继承的对象引入了一个基类

哇!停在那儿。这是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 }