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. An instantiation would fill in the variadic function's varargs, allowing the function to be nicely inlined. 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:
i开发者_开发知识库nt __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*}
. 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?
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).
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.
I strongly suspect the reason always inlining variadic function is unimplemented is because of the implementation of va_args assume standard stack layout. 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.
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
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")
.
精彩评论