4

为了绕过 GCC 在 libc++ 中未实现的始终内联可变参数函数,我想我可以将可变参数函数(如 snprintf,更准确地说是 *_l 变体)包装在可变参数模板中以实现类似的效果。实例化将填充可变参数函数的可变参数,从而使函数可以很好地内联。问题是,我不知道编写可变参数模板的第一件事,当然也不知道如何将模板参数转换为单独的参数。

我要替换的代码是以下形式:

int __sprintf_l(char *__s, locale_t __l, const char *__format, ...) {
  va_list __va;
  va_start(__va, __format);
  int __res = vsprintf_l(__s, __l, __format, __va);
  va_end(__va);
  return __res;
}

我想用以下形式替换它:

template<typename... Args>
int __sprintf_l(char *__s, locale_t __l, const char *__format, Args... args) {
  int __res = vsprintf_l(__s, __l, __format, args...);
  return __res;
}

这不起作用,因为扩展后args...无法转换typeva_list {aka char*}. 如果没有办法,我将不得不信任 Howard 并实现一个和两个参数的始终内联模板,这将有效地使所需代码量翻倍。

编辑:也许一种将其转换std::tupleargsva_list 的方法可以在这里工作?

4

3 回答 3

3

我认为您提出的问题令人困惑,所以让我重申一下。

您想使用可变参数模板编写一个模拟内联可变参数函数的函数。

这是不可能的。 va_args通常被实现为堆栈上第一个参数的 void* (请注意,正是由于这个原因,可变参数函数必须具有至少一个非可变参数)。

您需要操纵调用堆栈以将参数放在正确的位置。现在可能是可变参数模板函数的参数在堆栈上的位置与va_args想要的位置相同,但这需要模板函数不被内联。

我强烈怀疑总是内联可变参数函数未实现的原因是因为 va_args 的实现假定标准堆栈布局。为了让编译器内联该函数,它需要分配堆栈空间并复制参数。它唯一可以保存的是实际jmpret说明。

可以做到,但内联的一半好处消失了。此外,编译器必须将参数传递代码(即编译器代码)提升到更通用的位置,以便与常规函数调用一起使用,作为可变参数函数的强制内联。换句话说,它使控制流显着复杂化,而收益很小甚至没有。

于 2011-09-27T19:15:58.753 回答
1

You could implement your own sprintf_l

int __noninlined_sprintf_l(char *__s, locale_t __l, const char *__format, ...) {
  va_list __va;
  va_start(__va, __format);
  int __res = vsprintf_l(__s, __l, __format, __va);
  va_end(__va);
  return __res;
}

And call that instead

template<typename... Args>
int __sprintf_l(char *__s, locale_t __l, const char *__format, Args... args) {
  int __res = __noninlined_sprintf_l(__s, __l, __format, args...);
  return __res;
}
于 2011-09-25T14:30:20.343 回答
1
template<typename... T>
int
variadic(char* s, locale_t locale, const char* format, T&&... t)
{
    return __sprintf_l(s, locale, format, std::forward<T>(t)...);
}

然后调用variadic(s, l, "%d %s", 42, "Hello")将导致调用__sprintf_l(s, l, "%d %s", 42, "Hello").

于 2011-09-25T23:54:00.523 回答