我目前正在将报告库作为大型项目的一部分。它包含一组日志记录和系统消息功能。我正在尝试利用预处理器宏去除严格用于调试的函数调用的子集,以及函数定义和实现本身,使用条件编译和函数(如宏定义为无)(类似于{{的方式)如果定义了assert()
,则会删除1}}调用。我遇到了问题。我更喜欢完全限定命名空间,我发现它提高了可读性;我将报告函数包装在命名空间中。因为冒号字符不能是宏令牌的一部分,所以我无法在剥离函数调用时包含命名空间。如果我单独定义函数,我最终会使用DEBUG
。我考虑过只使用条件编译来阻止这些函数的函数代码,但我担心编译器可能无法正确地优化空函数。
Namespace::
有关如何使用预处理器处理命名空间限定符的任何想法吗?
关于,问题,只是删除功能代码的想法?
还有其他方法可以实现我的目标吗?
答案 0 :(得分:2)
这是我几个月前写一个类似的图书馆时的表现。是的,您的优化器将删除空的内联函数调用。如果将它们声明为脱节(不在头文件中),除非使用LTO,否则编译器不会内联它们。
namespace Reporting
{
const extern std::string logFileName;
void Report(std::string msg);
void Report(std::string msg, std::string msgLogAdd);
void Log(std::string msg);
void Message(std::string msg);
#ifdef DEBUG
inline void Debug_Log(std::string message) { return Log(message); }
inline void Debug_Message(std::string message) { return Message(message); }
inline void Debug_Report(std::string message) { return Report(message); }
inline void Debug_Assert(bool test, std::string message) { /* Not sure what to do here */ }
#else
inline void Debug_Log(std::string) {}
inline void Debug_Message(std::string) {}
inline void Debug_Report(std::string) {}
inline void Debug_Assert(std::string) {}
#endif
};
就像旁注一样,除非你需要制作副本,否则不要按值传递字符串。请改用const引用。它可以防止对每个函数调用的字符串进行昂贵的分配+ strcpy。
编辑:实际上,现在我考虑一下,只需使用const char *。看一下装配体,它的速度要快得多,特别是对于空功能体。
海湾合作委员会在-O1优化了这一点,我不认为这有很多问题:
clark@clark-laptop /tmp $ cat t.cpp
#include <cstdio>
inline void do_nothing()
{
}
int main()
{
do_nothing();
return 0;
}
clark@clark-laptop /tmp $ g++ -O1 -S t.cpp
clark@clark-laptop /tmp $ cat t.s
.file "t.cpp"
.text
.globl main
.type main, @function
main:
.LFB32:
.cfi_startproc
movl $0, %eax
ret
.cfi_endproc
.LFE32:
.size main, .-main
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",@progbits
经过一些调整后,如果你使用const char *,not std :: string或const std :: string&amp;,它似乎只会被完全删除。这是const char *的组件:
clark@clark-laptop /tmp $ cat t.cpp
inline void do_nothing(const char*)
{
}
int main()
{
do_nothing("test");
return 0;
}
clark@clark-laptop /tmp $ g++ -O1 -S t.cpp
clark@clark-laptop /tmp $ cat t.s
.file "t.cpp"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
movl $0, %eax
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",@progbits
这里有const std :: string&amp; ...
.file "t.cpp"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "test"
.text
.globl main
.type main, @function
main:
.LFB591:
.cfi_startproc
subq $24, %rsp
.cfi_def_cfa_offset 32
leaq 14(%rsp), %rdx
movq %rsp, %rdi
movl $.LC0, %esi
call _ZNSsC1EPKcRKSaIcE
movq (%rsp), %rdi
subq $24, %rdi
cmpq $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
je .L11
movl $_ZL22__gthrw_pthread_cancelm, %eax
testq %rax, %rax
je .L3
movl $-1, %eax
lock xaddl %eax, 16(%rdi)
jmp .L4
.L3:
movl 16(%rdi), %eax
leal -1(%rax), %edx
movl %edx, 16(%rdi)
.L4:
testl %eax, %eax
jg .L11
leaq 15(%rsp), %rsi
call _ZNSs4_Rep10_M_destroyERKSaIcE
.L11:
movl $0, %eax
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE591:
.size main, .-main
[Useless stuff removed...]
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",@progbits
巨大的差异,嗯?
答案 1 :(得分:0)
我不确定我是否完全理解你的问题。请问有以下帮助吗?
namespace X
{
namespace{int dummy;}
void debug_check(int);
}
#ifdef DEBUG
#define DEBUG_CHECK(ARG) debug_check(ARG)
#else
#define DEBUG_CHECK(ARG) dummy // just ignore args
#endif
int main()
{
X::DEBUG_CHECK(1);
}
此解决方案可能无效,因为它可以生成“无效声明”警告。一个可能更好的解决方案是在函数声明中吞噬名称空间前缀:
// debug_check and "#ifdef DEBUG" part omitted
namespace X
{
typedef void dummy_type;
}
namespace Y
{
typedef void dummy_type;
}
typedef void dummy_type;
#define DEBUG(X) dummy_type dummy_fn();
int main()
{
X::DEBUG(1);
Y::DEBUG(2);
X::DEBUG(3);
Y::DEBUG(4);
DEBUG(5);
DEBUG(6);
};
只要dummy_type的任何定义产生相同的类型,这应该是合法的,因为typedef不是不同的类型。
答案 2 :(得分:0)
您可以将您的日志记录功能替换为什么都不做的功能,不是吗?
答案 3 :(得分:0)
我知道这些问题已经应答了很久,但是当我将日志宏放入命名空间时,我遇到了这个问题。您建议使用空函数和优化级别。克拉克盖布尔斯让我思考,因为使用const char*
或const std::string&
的结果不同。以下代码在没有启用优化级别的情况下没有对程序集进行任何合理更改:
#include <iostream>
#undef _DEBUG // undefine to use __NOJOB
namespace Debug
{
typedef void __NOJOB;
class Logger
{
public:
static void Log( const char* msg, const char* file, int line )
{
std::cout << "Log: " << msg << " in " <<
file << ":" << line << std::endl;
}
};
}
#ifdef _DEBUG
#define Log( msg ) Logger::Log( msg, __FILE__, __LINE__ );
#else
#define Log( msg )__NOJOB(0);
#endif
int main()
{
Debug::Log( "please skip me" );
return 0;
}
按http://assembly.ynh.io/创建了程序集:
main:
.LFB972:
.cfi_startproc
0000 55 pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
0001 4889E5 movq %rsp, %rbp
.cfi_def_cfa_register 6 // <- stack main
// no code for void( 0 ) here
0004 B8000000 movl $0, %eax // return
00
0009 5D popq %rbp // -> end stack main
.cfi_def_cfa 7, 8
000a C3 ret
也许我弄错了或者弄错了什么?很高兴听到你的消息。