gpt4 book ai didi

c++ - 如何在 C++ 中的嵌套词法作用域可访问的作用域中声明静态信息?

转载 作者:行者123 更新时间:2023-12-02 03:00:24 26 4
gpt4 key购买 nike

我想声明范围的标识符,该标识符将用于自动填充最内部范围内任何日志记录语句的字段。它们通常(但并非总是)(例如 lambda、使用 {} 引入的 block )与封闭 block 的“名称”匹配。

用法看起来像这样:

namespace app {

LOG_CONTEXT( "app" );

class Connector {
LOG_CONTEXT( "Connector" );
void send( const std::string & msg )
{
LOG_CONTEXT( "send()" );
LOG_TRACE( msg );
}
};

} // namespace app

// not inherited
LOG_CONTEXT( "global", false );

void fn()
{
LOG_DEBUG( "in fn" );
}

int main()
{
LOG_CONTEXT( "main()" );
LOG_INFO( "starting app" );
fn();
Connector c;
c.send( "hello world" );
}

结果类似于:

[2018-03-21 10:17:16.146] [info] [main()] starting app
[2018-03-21 10:17:16.146] [debug] [global] in fn
[2018-03-21 10:17:16.146] [trace] [app.Connector.send()] hello world

我们可以通过定义LOG_CONTEXT宏来声明一个结构体来获取最内部的作用域。然后在 LOG_* 宏中,我们调用它的静态方法来检索名称。我们将整个事情传递给可调用对象,例如:

namespace logging {

spdlog::logger & instance()
{
auto sink =
std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
decltype(sink) sinks[] = {sink};
static spdlog::logger logger(
"console", std::begin( sinks ), std::end( sinks ) );
return logger;
}

// TODO: stack-able context
class log_context
{
public:
log_context( const char * name )
: name_( name )
{}

const char * name() const
{ return name_; }

private:
const char * name_;
};

class log_statement
{
public:
log_statement( spdlog::logger & logger,
spdlog::level::level_enum level,
const log_context & context )
: logger_ ( logger )
, level_ ( level )
, context_( context )
{}

template<class T, class... U>
void operator()( const T & t, U&&... u )
{
std::string fmt = std::string( "[{}] " ) + t;
logger_.log(
level_,
fmt.c_str(),
context_.name(),
std::forward<U>( u )... );
}

private:
spdlog::logger & logger_;
spdlog::level::level_enum level_;
const log_context & context_;
};

} // namespace logging

#define LOGGER ::logging::instance()

#define CHECK_LEVEL( level_name ) \
LOGGER.should_log( ::spdlog::level::level_name )

#define CHECK_AND_LOG( level_name ) \
if ( !CHECK_LEVEL( level_name ) ) {} \
else \
::logging::log_statement( \
LOGGER, \
::spdlog::level::level_name, \
__log_context__::context() )

#define LOG_TRACE CHECK_AND_LOG( trace )
#define LOG_DEBUG CHECK_AND_LOG( debug )
#define LOG_INFO CHECK_AND_LOG( info )
#define LOG_WARNING CHECK_AND_LOG( warn )
#define LOG_ERROR CHECK_AND_LOG( err )
#define LOG_CRITICAL CHECK_AND_LOG( critical )

#define LOG_CONTEXT( name_ ) \
struct __log_context__ \
{ \
static ::logging::log_context context() \
{ \
return ::logging::log_context( name_ ); \
} \
}

LOG_CONTEXT( "global" );

我陷入困境的是构建在定义最内部的__log_context__时使用的上下文堆栈。我们可以使用不同名称的结构和宏约定来添加 1 或 2 个级别(例如 LOG_MODULE 可以定义 __log_module__),但我想要一个更通用的解决方案。以下是我能想到的使事情变得更容易的限制:

  1. 范围嵌套级别可能有合理的限制,但用户不必提供当前级别/代码可以在不更改的情况下移动到不同的范围。也许 16 个级别就足够了(这为我们 orgname::app::module::subsystem::subsubsystem::detail::impl::detail::util 提供了一些空闲空间......)
  2. 一个范围内的下一级范围的数量(在单个翻译单元中)可能是有限的,但应该比 1 的值大得多。也许 256 是合理的,但我确信有人会有反例。
  3. 理想情况下,相同的宏可以用于任何上下文。

我考虑了以下方法:

  1. 使用 __parent_context__ = __log_context__;结构 __log_context__ ...

    希望 __parent_context__ 获取外部上下文,但我收到编译器错误,表明类型名称必须明确引用同一范围内的单个类型。此限制仅在类主体中使用时适用,否则这适用于函数和命名空间。

  2. 跟踪适用于某个范围的结构,例如 boost::mpl::vector

    教程中的示例让我相信我会遇到与 1 中相同的问题,因为被插入后的 vector 需要给出一个不同的名称,需要在嵌套范围中专门引用。

  3. 使用预处理器计数器生成适用的外部作用域的名称。

    这在上面的简单用法示例中是可行的,但如果相应类外部的命名空间或方法定义中存在不连续声明,则会失败。

如何在嵌套范围内访问此信息?

最佳答案

好的,我找到了解决方案。

诀窍在于,外部作用域中可见的 vardecltype(var) 将解析为该外部作用域 var 的类型即使我们稍后在同一范围内定义 var 。这允许我们隐藏外部类型,但仍然可以通过外部类型的未使用的变量来访问它,同时允许我们定义要在内部作用域中访问的同名变量。

我们的总体结构如下

struct __log_context__
{
typedef decltype(__log_context_var__) prev;
static const char * name() { return name_; }
static ::logging::log_context context()
{
return ::logging::log_context(
name(), chain<__log_context__>::get() );
}
};
static __log_context__ __log_context_var__;

唯一的其他细节是,我们在迭代上下文链时需要一个终止条件,因此我们使用 void* 作为哨兵值,并在用于构造输出的帮助器类中对其进行专门化字符串。

decltype 需要 C++11,并允许将本地类传递给模板参数。

#include <spdlog/spdlog.h>

namespace logging {

spdlog::logger & instance()
{
auto sink =
std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
decltype(sink) sinks[] = {sink};
static spdlog::logger logger(
"console", std::begin( sinks ), std::end( sinks ) );
return logger;
}

class log_context
{
public:
log_context( const char * name,
const std::string & scope_name )
: name_ ( name )
, scope_( scope_name )
{}

const char * name() const
{ return name_; }

const char * scope() const
{ return scope_.c_str(); }

private:
const char * name_;
std::string scope_;
};

class log_statement
{
public:
log_statement( spdlog::logger & logger,
spdlog::level::level_enum level,
const log_context & context )
: logger_ ( logger )
, level_ ( level )
, context_( context )
{}

template<class T, class... U>
void operator()( const T & t, U&&... u )
{
std::string fmt = std::string( "[{}] " ) + t;
logger_.log(
level_,
fmt.c_str(),
context_.scope(),
std::forward<U>( u )... );
}

private:
spdlog::logger & logger_;
spdlog::level::level_enum level_;
const log_context & context_;
};

} // namespace logging

// Helpers for walking up the lexical scope chain.
template<class T, class Prev = typename T::prev>
struct chain
{
static std::string get()
{
return (chain<Prev, typename Prev::prev>::get() + ".")
+ T::name();
}
};

template<class T>
struct chain<T, void*>
{
static std::string get()
{
return T::name();
}
};

#define LOGGER ::logging::instance()

#define CHECK_LEVEL( level_name ) \
LOGGER.should_log( ::spdlog::level::level_name )

#define CHECK_AND_LOG( level_name ) \
if ( !CHECK_LEVEL( level_name ) ) {} \
else \
::logging::log_statement( \
LOGGER, \
::spdlog::level::level_name, \
__log_context__::context() )

#define LOG_TRACE CHECK_AND_LOG( trace )
#define LOG_DEBUG CHECK_AND_LOG( debug )
#define LOG_INFO CHECK_AND_LOG( info )
#define LOG_WARNING CHECK_AND_LOG( warn )
#define LOG_ERROR CHECK_AND_LOG( err )
#define LOG_CRITICAL CHECK_AND_LOG( critical )

#define LOG_CONTEXT_IMPL(prev_type,name_) \
struct __log_context__ \
{ \
typedef prev_type prev; \
static const char * name() { return name_; } \
static ::logging::log_context context() \
{ \
return ::logging::log_context( \
name(), chain<__log_context__>::get() ); \
} \
}; \
static __log_context__ __log_context_var__

#define LOG_CONTEXT(name_) \
LOG_CONTEXT_IMPL(decltype(__log_context_var__),name_)

#define ROOT_CONTEXT(name_) \
LOG_CONTEXT_IMPL(void*,name_)

// We include the root definition here to ensure that
// __log_context_var__ is always defined for any uses of
// LOG_CONTEXT.
ROOT_CONTEXT( "global" );

它与我最初的帖子中的代码近似

#include <logging.hpp>

namespace app {

LOG_CONTEXT( "app" );

class Connector {
LOG_CONTEXT( "Connector" );

public:
void send( const std::string & msg )
{
LOG_CONTEXT( "send()" );
LOG_TRACE( msg );
}
};

} // namespace app

void fn()
{
LOG_DEBUG( "in fn" );
}

int main()
{
LOG_CONTEXT( "main()" );
LOGGER.set_level( spdlog::level::trace );
LOG_INFO( "starting app" );
fn();
app::Connector c;
c.send( "hello world" );
}

产量

[2018-03-22 22:35:06.746] [console] [info] [global.main()] starting app
[2018-03-22 22:35:06.747] [console] [debug] [global] in fn
[2018-03-22 22:35:06.747] [console] [trace] [global.app.Connector.send()] hello world

根据需要。

问题示例中提到的有条件继承外部作用域留作练习。

关于c++ - 如何在 C++ 中的嵌套词法作用域可访问的作用域中声明静态信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49413154/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com