Вариативные аргументы в массивах символов - PullRequest
1 голос
/ 13 марта 2019

Я хочу вызвать функцию, которая создает меню с чем-то вроде этого:

Mode_Menu("Item 1", "Item A1c", "Item Fred", ..... "Item xxx"), где n может быть любым разумным числом, и каждый элемент может иметь произвольную длину.

Я пробовал следующее (игнорируйте числа в Offer_Mode - это просто координата Y на ЖК-дисплее)

void Mode_Menu(char *m1, ...)
    {
        va_list argp;
        va_start(argp, *m1);
        Offer_Mode(2, m1++);
        Offer_Mode(33, m1++);
        Offer_Mode(64, *m1++;
        Offer_Mode(97, *m1++);
        Offer_Mode(130, *m1++);        
    }

Но я получаю

Item 1
tem 1
em 1
m 1
 1

т.е. указатель перемещается вдоль первого элемента и даже не видит второй элемент в вызове функции.

Я перепробовал все заклинания, которые мог придумать, чтобы перейти к следующим элементам, используя такие вещи, как *m1[] или m1[2] в определении функции и в va_start, но все, что я пробую, выдает ошибки.

Может кто-нибудь указать мне правильный способ сделать это, пожалуйста?

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

Ответы [ 3 ]

1 голос
/ 13 марта 2019

Я повторяю то, что я уже сказал в комментарии:

В C ++ я бы предпочел переменные шаблоны или, возможно, предоставление аргументов в контейнере (например, std :: vector). Если это вообще не вариант, я бы использовал va_arg для доступа к аргументам. И, пожалуйста, не забудьте va_end (). Обработка переменных аргументов в макросах va_ может быть очень зависимой от платформы и хрупкой, если не используется с осторожностью.

Однако, (только если вспомнить мои собственные попытки напомнить printf() в далеком прошлом) пример:

#include <iostream>
#include <cstdarg>

void printMenu(int len, ...)
{
  va_list argp;
  va_start(argp, len);
  for (int i = 0; i < len; ++i) {
    const char *item = va_arg(argp, const char*);
    std::cout << '[' << (i + 1) << "]: " << item << '\n';
  }
  va_end(argp);
}

int main()
{
  printMenu(3, "Item 1", "Item A1c", "Item Fred");
  return 0;
}

Выход:

[1]: Item 1
[2]: Item A1c
[3]: Item Fred

Демонстрация в реальном времени на coliru

Примечания:

  1. Одна трудность состоит в том, чтобы правильно распознать число переменных аргументов. Я использовал счет для этого. Другим часто используемым вариантом является отметка конца (например, с аргументом nullptr). Что касается printf(), число ожидаемых аргументов напрямую зависит от содержимого аргумента форматера. (Следовательно, printf() считаются хрупкими и небезопасными.)

  2. В качестве аргументов можно ожидать только определенные типы. Подробнее об этом здесь: Variadic arguments - Преобразования по умолчанию

1 голос
/ 13 марта 2019

Вы можете решить, указав количество дополнительных передаваемых аргументов.

Каждый дополнительный аргумент должен быть "извлечен" из списка.

Я использовал cout, потому что я не знаючто Offer_mode делает.

#include<iostream>
#include <stdarg.h>
void Mode_Menu(char *m1, unsigned int count,...);

int main(int argc, char* argv[])
{
   Mode_Menu("Item 1", 3,"Item A1c", "Item Fred", "Item xxx");
  return 0;
}


void Mode_Menu(char *m1, unsigned int count, ...)
{
  va_list ap;
  va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
  for (int j = 0; j < count; j++)
  {
    std::cout<<va_arg(ap, char*)<<std::endl; //ap automatically incremented
  }
  va_end(ap);
}
1 голос
/ 13 марта 2019

Интерфейс C для шаблонов Vardiac делает это:

void Mode_Menu(char *arg, ...)
    {
        va_list argp;
        va_start(argp, &arg);
        Offer_Mode(2, arg);
        arg=va_arg(argp, char*)
        Offer_Mode(33, arg);
        arg=va_arg(argp, char*)
        Offer_Mode(64, arg);
        ....
        va_end(argp);
    }

РЕДАКТИРОВАТЬ: фактический код должен иметь способ узнать конец параметров.Это может быть первый целочисленный аргумент, строковая информация (например, формат printf или сопоставление с образцом в строках) или нулевой аргумент (последний параметр).

Способ C ++ (с SFINAE для проверки правильности типа)

    template <typename ...T>
    std::enable_if_t<(std::is_same_v<T, char>&&...)>
    Mode_Menu(const T*... args)
    {
        const char * texts[] = { args... };

        Offer_Mode(2, texts[0]);

        Offer_Mode(33, texts[1]);

        Offer_Mode(64, texts[2]);
        ....
    }

EDIT 2: вариант шаблона уже содержит информацию о размере, как в sizeof...(T), так и std::size(texts).В связи с этим, в отличие от варианта C, нет необходимости усердно работать, чтобы обнаружить последнюю строку.

...