Variadic arguments on char arrays

c variadic arguments
std::function variadic arguments
variadic arguments c
variadic function
variadic template constructor
function with variable number of arguments in c++
char pointer array in c
c forward variadic arguments

I want to call a function that puts up a menu with something like this :

Mode_Menu("Item 1", "Item A1c", "Item Fred", ..... "Item xxx") where n could be any reasonable number and each item can be of random length.

I've tried the following (ignore the numbers in Offer_Mode - that's just the Y coordinate on the LCD)

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

But what I get is

Item 1
tem 1
em 1
m 1
 1

i.e. the pointer is moving along the first element and never even sees the second element in the function call.

I've tried all the incantations I can think of to step along to the following elements using things like *m1[] or m1[2] in the function definition and in va_start but everything I try throws up an errors.

Can somebody point me to the correct way to do this please?

(and I have searched extensively first so please don't mark as duplicate. There's lot of examples using integers but none that I can find using char arrays).

The C interface for vardiac templates makes it:

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

EDIT: The actual code should have a way to find out the end of the parameters. It can be by an integer first argument, from string information (like printf format, or by pattern matching in the strings), or by a nullptr sentinel (last parameter).

The C++ way (with SFINAE to check type correctness)

    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: The template variant already contains size information, both in sizeof...(T) and std::size(texts). Due to that, unlike with the C variant, there is no need to work hard to detect the last string.

Variadic arguments, the function declared as follows int printx(const char* fmt. When a variadic function is called, after lvalue-to-rvalue, array-to-pointer, and� Variadic templates can also be used to create functions that take variable number of arguments. They are often the better choice because they do not impose restrictions on the types of the arguments, do not perform integral and floating-point promotions, and are type safe.

You can solve specifying the number of additional arguments you are passing.

Each additional argument must be "extracted" from the list.

I used cout because I do not know what Offer_mode does.

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

Variable Length Arguments, %varargs(10,char *arg = NULL) execlp; int execlp(const char *path, const char *arg1, . Then, the array is assigned to $1 (recall that this is the void * variable� Variadic templates. One of the new features of C++11 is variadic templates.Finally, there's a way to write functions that take an arbitrary number of arguments in a type-safe way and have all the argument handling logic resolved at compile-time, rather than run-time.

I repeat what I already said in comment:

In C++, I would prefer variadic templates or, may be, providing arguments in a container (like e.g. a std::vector). If that's not an option at all, then I would use va_arg to access arguments. And, please, don't forget the va_end(). Processing variadic arguments in va_ macros can be very platform dependent and fragile if not used with care.

However, (just in remember of my own tries to resemble printf() in the far past) a sample:

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

Output:

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

Live Demo on coliru

Notes:

  1. One difficulty is to recognize the number of variadic arguments correctly. I used a count for this. Another often used option is to remark the end (e.g. with a nullptr argument). Concerning printf(), the number of expected arguments depends directly from the contents of formatter argument. (Hence, the printf() are considered as fragile and unsafe.)

  2. There are only certain types which can be expected for arguments. More about this here: Variadic arguments - Default conversions

C Strings (Arrays vs. Pointers), A character array can have more characters than the abstract string held in it, array variable versus using this array notation for the parameter of a function. A comma-separated list of arguments of the type of the array elements. An array of arguments of the specified type. No arguments. If you send no arguments, the length of the params list is zero. Example. The following example demonstrates various ways in which arguments can be sent to a params parameter.

Character Array and Character Pointer in C, In this chapter, we will study the difference between character array and whose formal argument accepts an array of characters or a character pointer. for string literal "Hello World" and 4 extra bytes for pointer variable ptr . The number of arguments can be found out using a.length, the way we find the length of an array in Java. Note: A method can have variable length parameters with other parameters too, but one should ensure that there exists only one varargs parameter that should be written last in the parameter list of the method declaration.

Parameters and Arguments, Except for functions with variable-length argument lists, the number of The value of argc is nonnegative. argv: Pointer to an array of character strings that� (Also the reason why a VARIADIC parameter must come last in the parameter list: else the call could be ambiguous.) If you don't need this feature, use a plain varchar[] parameter without VARIADIC in the function definition to begin with. Related: How to use an array as argument to a VARIADIC function in PostgreSQL?

Array of Srings --- the command line argument, char *x[10]; // x is an array of 10 reference variables // -- each variable x[i] is a ref. to a char var. // ==> x is an array of strings !!! Example using the arguments in� In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages. The term variadic is a neologism, dating back to 1936. The term was not widely used until the 1970s.

Comments
  • Why wouldn't you pass a std::vector<std::string> or similarly std::array instead?
  • In C++, I would prefer variadic templates or, may be, providing arguments in a container (like e.g. a std::vector). If that's not an option at all, then I would use va_arg to access arguments. And, please, don't forget the va_end(). Processing variadic arguments in va_ macros can be very platform dependent and fragile if not used with care.
  • Currently, you simply don't use the variadic arguments at all... You'll need va_arg for (instead of incrementing first pointer argument). Be aware that you need to find a way to get the end of the arguments list yourself. You should avoid (classic) variadic functions (as @Scheff recommended already), though, they are pretty unsafe.
  • A container isn't an option as the menu line needs to be edited directly by people who aren't C++ experts (aka musicians). I need to isolate them from all the complexities of the laguage. But yes, it was varg I was missing. Thanks to Scheff and Aconcagua
  • How is it possible to check the size of the texts array? In your example, if there is only one argument, you're going to have an error at texts[1].
  • @Pierre there isn't one argument. It is a parameter pack expansion, with as much parameters as there are in the input. You can check their number at compile time with sizeof...(T), or std:size(texts)
  • See en.cppreference.com/w/cpp/language/…
  • Variadic function variant: Should be mentioned that we yet need some way to find the end of the arguments list. My personal variant would be terminating the arguments with a null pointer as sentinel...
  • @Aconcagua for the C variant, I fully agree but it wasn't part of the question
  • You forgot to use va_end. Why is it important: stackoverflow.com/questions/587128/…
  • Agree on the end - I think musicians will understand what "End of Menu" means and leave it there :-)