Calling a function with a literal list of strings?

J

Joakim Hove

Hello,

I have a function like this:


/* It actually does something else .... */
void func (int size, const char ** string_list) {
int i;
for (i=0; i < size; i++)
printf("String number %d: %s \n",i,string_list);
}

And would like to (be able) to call it with a literal list of strings,
something like:

func(3 , {"String1" , "Hello", "The last string ..."});

Is that possible? I have tried various casts like (const char **),
(const char [3][]) and (const char [3]*) but to no avail. I am using
gcc with std=gnu99, i.e. I am more than happy to accept C99 features.

Thank you - Joakim Hove
 
C

Chris Torek

I have a function like this:

/* It actually does something else .... */
void func (int size, const char ** string_list) {
int i;
for (i=0; i < size; i++)
printf("String number %d: %s \n",i,string_list);
}

And would like to (be able) to call it with a literal list of strings,
something like:

func(3 , {"String1" , "Hello", "The last string ..."});

Is that possible? ... I am more than happy to accept C99 features.


If it were more complicated you might be out of luck, but because
C's string literals are kind of funny and sort-of-primitive, you
are in luck:

void f(void) {
func(3, (const char *[]){"1", "2", "3"});
/* you can put in the array size too: (const char *[3]) */
}
I have tried various casts like (const char **),
(const char [3][]) and (const char [3]*) but to no avail.

The thing in front of the brace list *looks* exactly like a cast,
but is not actually a cast. Instead, it is the type-specifier
part of a "compound literal".

Because the call to func() is within a block (all function calls
in C are always inside a block -- you cannot call a function
outside the outermost block of some other function), the object
created by such a call has automatic duration. In practice, this
means you get runtime code to create the array. You may get
slightly better performance if you just give in and create an
actual object, which you can then make "static":

void f(void) {
static const char *arg[] = {"1", "2", "3"};
func(3, arg);
}

In practice, this eliminates the runtime code: the array is
created at compile-time and exists before main() even gets
called. (In theory, the object could be created exactly once
when f() got called, since you would not be able to tell that
this happened. But only in theory is theory equivalent to
practice. :) )

Of course, if you do this, you can also automatically count the
number of elements in the array:

#define NELEM(array) (sizeof array / sizeof *(array))
void f(void) {
static const char *arg[] = {"1", "2", "3"};
func(NELEM(arg), arg);
}

and of course all of this now works in C89. The only drawback is
that you must invent a name for each call, and the initialization
tends to move away from the call:

void f(void) {
static const char *argsA[] = {"1", "2", "3"};
static const char *argsB[] = {"Hello", "world"};

func(NELEM(argsA), argsA);
func(NELEM(argsB), argsB);
}

If you resort to C99's "declare anywhere" and variable argument
macros, you can solve both of those with a macro, provided you are
willing to require that each call be on a separate line:

#define PASTE(x, y) x##y
#define XPASTE(x, y) PASTE(x, y)
#define DOIT(...) \
static const char *XPASTE(args, __LINE__)[] = __VA_ARGS__; \
func(NELEM(XPASTE(args, __LINE__)), XPASTE(args, __LINE__))

(Here the PASTE and XPASTE auxiliary macros cause __LINE__ to get
expanded before token-pasting, and __VA_ARGS__ extracts what the
preprocessor sees as multiple arguments, since the comma that
separates the initializers is not protected by parentheses.)
 
J

Joakim Hove

Dear Chris,

thank you *very much* for a thorough and informative answer - I have
learned a lot today.
The thing in front of the brace list *looks* exactly like a cast,
but is not actually a cast. Instead, it is the type-specifier
part of a "compound literal".

The type-specifier of a compound literal - neat!
Because the call to func() is within a block (all function calls
in C are always inside a block -- you cannot call a function
outside the outermost block of some other function), the object
created by such a call has automatic duration. In practice, this
means you get runtime code to create the array. You may get
slightly better performance if you just give in and create an
actual object, which you can then make "static":

I won't give in - I just love the compact calls like:

func (3 , (const char *[]) {"S" , "S2" , "..."});

which I can now use - thanks to your help. The call will be issued in
the initialization part of the code, and performance is irrelevant.

Regards

Joakim Hove
 
M

Martin

Chris Torek said:
If it were more complicated you might be out of luck, but because
C's string literals are kind of funny and sort-of-primitive, you
are in luck:

void f(void) {
func(3, (const char *[]){"1", "2", "3"});
/* you can put in the array size too: (const char *[3]) */
}

gcc version 3.0.4 emits the warning:

ISO C89 forbids compound literals
 
C

Chris Torek

Chris Torek said:
If it were more complicated you might be out of luck, but because
C's string literals are kind of funny and sort-of-primitive, you
are in luck:

void f(void) {
func(3, (const char *[]){"1", "2", "3"});
/* you can put in the array size too: (const char *[3]) */
}

gcc version 3.0.4 emits the warning:

ISO C89 forbids compound literals

Indeed. (The message might be slightly better-phrased if it said
"ISO C90" or "ANSI C89", but the meaning is clear either way.)

However, before the stuff I wrote above, I quoted the following
sentence:

Compound literals are a C99 feature. That GCC warns about them in
its default, neither-C89-nor-C99 not-quite-a-C-compiler compilation
mode (and/or in its C89-specific mode), is probably a good thing.

(I also described how to do the trick without using C99 features, or
with different C99 features. If you use C99 features, you must of
course have support for those in your compiler.)
 
M

Martin

Indeed. (The message might be slightly better-phrased if it said
"ISO C90" or "ANSI C89", but the meaning is clear either way.)

However, before the stuff I wrote above, I quoted the following
sentence:

Ah, right. I compile using

gcc -Wall -pedantic -ansi

so the warning is not surprising. And it is a warning: the code does compile
and run. But I wouldn't let that success stop me disallowing such constructs
being used at my workplace in a C89 compiler.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top