function 'xxx' can never be inlined because it uses variable argument lists
原因
一句话概括:使用va_list
, va_start
, va_end
的函数无法强制内联,即具有类似如下原型的函数无法被标记为inline __attribute__((always_inline))
或__forceinline
(除非你不用参数的变长部分,那也就没必要这样定义)。
void f(char* fmt, ...);
从底层来看,inline的原理是编译时展开,如果允许调用va_xx
的函数被内联,那么获取到的将是展开位置的变长参数列表(而且va_start
和va_end
事实上是宏而非函数),可能不符合预期行为。
GPT:
- 可变参数 (
...
) 的 获取机制 是基于底层 ABI 的。 va_start()
、va_arg()
、va_end()
都依赖当前调用帧(调用栈上的位置、寄存器布局)。- 编译器需要一个稳定的调用边界来解析这些参数,必须按照标准调用约定处理。
一旦你试图内联这样的函数:
- 编译器发现,调用点的上下文未必能直接“展开”这些 ABI 操作。
- 尤其跨平台、跨架构,变参处理有不同的实现细节。
因此,编译器通常明确禁止将带 ...
的函数(或调用 va_start
的函数)标记为 inline
或__forceinline
。
解决方案
1. 将格式化任务抛出给用户
利用snprintf()
格式化字符数组,如下:
inline __attribute__((always_inline)) void f(const char* content){
printf(content, NULL);
}
int main(){
char content[20];
snprintf(content, 20, "Value = %d", 42);
f(content);
}
该方案的优势在于通用性强,未涉及任何“新”特性,最纯粹的C方案。
2. 使用可变参数模板 + ...
(解包运算?) (C++11)
代码如下:
代码语言:cpp代码运行次数:0运行复制#include <tuple>
#include <cstdio>
template <typename... Args>
inline __attribute__((always_inline)) void sub_log(const char* format, Args&&... args) {
std::printf(format, args...);
}
template <typename... Args>
inline __attribute__((always_inline)) void log(const char* format, Args&&... args) {
sub_log(format, args...);
}
int main() {
log("%s %d\n", "The number is", 42);
return 0;
}