Приклеивание вариационного шаблона к вариационной функции - PullRequest
4 голосов
/ 25 сентября 2011

Пытаясь обойти невыполненные всегда встроенные вариадические функции GCC в libc ++, я подумал, что, возможно, мог бы обернуть вариадические функции (например, snprintf, точнее, вариант * _l) в шаблоне вариадий, чтобы добиться аналогичного эффекта. Инстанцирование заполняло бы переменные функции variadic, позволяя функции быть аккуратно встроенной. Проблема в том, что я не знаю в первую очередь о написании вариационных шаблонов и, конечно, не знаю, как превратить аргументы шаблона в отдельные аргументы.

Код, который я хочу заменить, имеет вид:

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..., который нельзя преобразовать в type в va_list {aka char*}. Если нет никакого способа, мне придется довериться Говарду и реализовать шаблоны с одним и двумя аргументами, всегда встроенные, что эффективно удвоит объем необходимого кода.

РЕДАКТИРОВАТЬ: возможно, будет работать способ преобразования std::tuple, который args в va_list, здесь?

Ответы [ 3 ]

3 голосов
/ 27 сентября 2011

Я думаю, что вопрос, который вы задаете, сбивает с толку, поэтому позвольте мне перефразировать его.

Вы хотите использовать переменные шаблоны для написания функции, которая имитирует встраивание переменной функции.

Это невозможно сделать. va_args часто реализуется как пустое значение * для первого параметра в стеке (обратите внимание, что именно по этой причине у функций с переменными значениями должен быть хотя бы один параметр без переменных).

Вам нужно будет управлять стеком вызовов, чтобы получить параметры в нужном месте. Теперь может случиться так, что аргументы функции шаблона variadic находятся в стеке в том же месте, что и va_args, и они потребуют, чтобы функция шаблона не была встроенной.

Я сильно подозреваю, что причина, по которой всегда встроенная функция переменной не реализуется, заключается в том, что реализация va_args предполагает стандартную компоновку стека. Чтобы компилятор встроил эту функцию, ему нужно было бы выделить место в стеке и скопировать параметры на месте. Единственное, что он мог бы сохранить - это действительные инструкции jmp и ret.

Это можно было бы сделать, но половина выгод от встраивания испарялась. Кроме того, компилятор должен будет поднять код передачи параметров (то есть код компилятора) в более общее место для использования с обычными вызовами функций в качестве принудительных встроенных функций с переменными числами. Другими словами, это значительно усложняет поток управления из-за небольшого или нулевого эффекта.

1 голос
/ 26 сентября 2011
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").

1 голос
/ 25 сентября 2011

Вы можете реализовать свой собственный 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;
}

И назовите это вместо

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...