简体   繁体   English

将可变参数模板粘贴到可变参数函数

[英]Gluing a variadic template to a variadic function

In an attempt to bypass GCC's unimplemented always-inlining variadic functions in libc++, I thought I could maybe wrap the variadic functions (like snprintf, more precisely, the *_l variant) in a variadic template to achieve a similar effect. 为了绕过GCC在libc ++中未实现的始终内联的可变参数函数,我想我可以将可变参数函数(如snprintf,更确切地说,* _l变量)包装在可变参数模板中以实现类似的效果。 An instantiation would fill in the variadic function's varargs, allowing the function to be nicely inlined. 实例化将填充可变参数函数的varargs,允许函数很好地内联。 The problem is, I don't know the first thing about writing variadic templates, and I certainly don't know how to turn the template arguments into seperate arguments. 问题是,我不知道编写可变参数模板的第一件事,我当然不知道如何将模板参数转换为单独的参数。

The code I'm looking to replace is of the form: 我想要替换的代码是以下形式:

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;
}

I'd like to replace is with something of the form: 我想用以下形式替换:

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;
}

This is not working, due to the expanded args... which cannot be converted to type to va_list {aka char*} . 这是行不通的,因为扩展的args...无法转换为typeva_list {aka char*} If there is no way, I'll have to trust Howard and implement one-, and two-argument always-inline templates, which would effectively double the amount of needed code. 如果没有办法,我将不得不相信霍华德并实现一个和两个参数的始终内联模板,这将有效地使所需代码的数量翻倍。

EDIT: perhaps a way to convert the std::tuple that args is into a va_list would work here? 编辑:也许一种方法将args转换为va_list的std::tuple可以在这里工作吗?

I think the question you're asking is confusing so let me restate it. 我认为你提出的问题令人困惑,所以让我重申一下。

You want to use variadic templates to write a function that simulates inlining a variadic function. 您希望使用可变参数模板来编写模拟内联可变函数的函数。

It cannot be done. 它无法完成。 va_args is often implemented as a void* to the first parameter on the stack (note variadic functions are required to have at least one non-variadic argument for exactly this reason). va_args通常被实现为堆栈中第一个参数的void *(注意,为了这个原因,变量函数至少需要有一个非可变参数)。

You would need to manipulate the call stack to get the parameters in the right place. 您需要操纵调用堆栈以获取正确位置的参数。 Now it might be the case that the variadic template function's arguments are on the stack in the same location as va_args would want them but that would require the template function to not be inlined. 现在可能的情况是,可变参数模板函数的参数位于堆栈中与va_args希望它们在同一位置, va_args需要模板函数不被内联。

I strongly suspect the reason always inlining variadic function is unimplemented is because of the implementation of va_args assume standard stack layout. 我强烈怀疑始终内联可变函数未实现的原因是因为va_args的实现假设标准堆栈布局。 For the compiler to inline that function it would need to allocate stack space and copy the parameters in place. 为了使编译器内联该函数,它需要分配堆栈空间并复制参数。 The only thing it would save is actual jmp and ret instructions. 它唯一能保存的是实际的jmpret指令。

It could be done, but half of the benefits of inlining evaporate. 它可以做到,但内联的一半好处蒸发了。 Further the compiler will have to hoist the parameter passing code (compiler code that is) to a more general location for use with regular function calls as forced inline of variadic functions. 此外,编译器必须将参数传递代码(编译器代码)提升到更一般的位置,以便与常规函数调用一起使用,作为可变函数的强制内联。 In other words it complicates the control flow significantly for small to no benefit. 换句话说,它使控制流程显着变得复杂,从小到无益。

You could implement your own sprintf_l 您可以实现自己的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;
}
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)...);
}

Then calling variadic(s, l, "%d %s", 42, "Hello") would result in a call to __sprintf_l(s, l, "%d %s", 42, "Hello") . 然后调用variadic(s, l, "%d %s", 42, "Hello")将导致调用__sprintf_l(s, l, "%d %s", 42, "Hello")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM