I looking to create a variadic function in C that allows to do something like this:
Send({1,2,3,4,5});
Send({4,5,2});
Send({1,1,1,1,1,1,1,1,1,1,1,1,1});
Note there is no length input and the array is placed inline and without any setup or creation of any variable
Currently i am using formal variadic option such as below (example from here), which is quite convenient but also prone to mistakes which are sometimes hard to debug such as forgetting to place the num_args (still compiles), placing the wrong number of elements etc.
int sum(int num_args, ...) {
int val = 0;
va_list ap;
int i;
va_start(ap, num_args);
for(i = 0; i < num_args; i++) {
val += va_arg(ap, int);
}
va_end(ap);
return val;
}
The typical way to define a function that operates on an arbitrary number of arguments of the same type is to use an array:
int sum(const int array[], size_t n)
{
int sum = 0;
while (n--) sum += array[n];
return sum;
}
That would mean that you would have to create an auxiliary array for each call and invoke the function, perhaps with a countof macro that uses sizeof to determine the size of the array and its first member.
As of C99, you can use compound literals to create such arrays:
int s = sum((int[]){1, 1, 2, 3, 5}, 5);
That might be more convenient and typesafe on the array elements, but still has the danger of getting the count wrong.
You can use a variadic macro to combine compound literals and the countof idiom:
#define SUM(...) sum((int[]){__VA_ARGS__}, \
sizeof((int[]){__VA_ARGS__}) / sizeof(int))
(The compound literal argument of the sizeof will only be evaluated for its size.)
Use it like this:
printf("%d\n", SUM(1, 2, 3, 4, 5));
printf("%d\n", SUM(200.0, 30.0, 5.0));
printf("%d\n", SUM());
(I'm not sure whether such a macro is useful, though. The sum example is contrived at best: sum(a, b, c) can be written as a + b + c. Perhaps a min or max macro for more than two arguments might be useful. In general, I find that I have the data I want in array form already when I work in C.)
If you don't want to pass length, you can use a sentinel value to mark the end. If you don't require full range of int, just use (for example) INT_MIN.
Send(1,1,1,1,1,1,1,1,1,1,1,1,1, INT_MIN);
And use that as end condition in your function's loop.
If you need full range of 32 bits, you could pass 64 bit integers so sentinel value can be outside the range of 32 bits. This will be a bit clunky though, you need to make all the parameters be 64 bit then, probably with suffix ll, and this will also make the code less portable/future proof.
One alternative is to use strings. Depending on your purpose, this can be quite robust, because compiler will warn about invalid format string. Here's the code I tested on gcc, and I belive it also works on clang. I hope it is sufficiently self-explanatory with the comments.
The __attribute__ ((format (printf, 1, 2))) part is quite important, it is the only thing making this robust, because it allows the compiler to check the arguments and produce warnings.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// helper function to get a single string,
// just like vsprintf, except
// returns malloc buffer containing the result text
char *buf_print(const char *fmt, va_list ap) {
int bufsize = 0;
char * buf = NULL;
{
// get the exact size needed for the string,
// ap can only be processed once, a copy is needed
va_list ap2;
va_copy(ap2, ap);
bufsize = vsnprintf(NULL, 0, fmt, ap2);
va_end(ap2);
}
if (bufsize >= 0) {
buf = malloc(bufsize + 1);
if (buf) {
bufsize = vsnprintf(buf, bufsize + 1, fmt, ap);
if (bufsize < 0) {
free(buf);
buf = NULL;
}
}
}
return buf;
}
// get next parseable integer from a string,
// skipping any leading invalid characters,
// and returning pointer for next character, as well as number in *out,
// or returning NULL if nothing could be parsed before end of string
char *parse_next_ll(char *buf, long long *out) {
char *endp = NULL;
while (*buf) {
*out = strtoll(buf, &endp, 10);
if (endp != buf) return endp; // something was parsed successfully
++buf; // nothing was parsed, try again from the next char
};
return NULL; // means *out does not contain valid number
}
// Go through all integers in formatted string, ignoring invalid chars,
// returning -1 on error, otherwise number of valid integers found
int
__attribute__ ((format (printf, 1, 2)))
Send(const char *fmt, ...)
{
int count = -1;
va_list ap;
va_start(ap, fmt);
char * const buf = buf_print(fmt, ap); // needs to be freed, so value must not be lost, so const
va_end(ap);
if (buf) {
count = 0;
long long number = 0;
char *s = buf;
while (s && *s) {
s = parse_next_ll(s, &number);
if (s) {
++count;
printf("Number %d: %lld\n", count, number);
}
}
free(buf);
}
return count;
}
int main()
{
int r = Send("1,2,3,4,%i", 1);
printf("Result: %d\n", r);
return 0;
}
Ouptut of that code:
Number 1: 1
Number 2: 2
Number 3: 3
Number 4: 4
Number 5: 1
Result: 5
Related
I am wondering can pure C do following pseudo code?
for(int i = 0; i < N; i++)
func( Multi("str",i));
I know the feature char *tmp = "str1" "str1" and tried to combine that and macro. But, the only way I come up with is define several macro with different repeat times. My method is bad for concise, are there better method ?
edit:
expect Multi can return "str" * i times
e.g. char *tmp = Multi("str",3); // now tmp is "strstrstr"
Not if you expect to be able to use the run-time value of a variable to control the number of repetitions (unless the range of values of that variable is small and known at compile-time).
Macro expansion and literal string concatenation are done as phases during the compilation, before the executable has been produced. The program doesn't yet exist, and certainly cannot be run. The macro preprocessor only sees a variable as an identifier inside the text of the program.
If you will always use a literal integer, then it is possible to do the expansion with the macro preprocessor, although it does indeed require a lot of macros. There are some macro libraries which can help.
If you know the maximum number of repetitions (and have some runtime mechanism to verify that the limit is not exceeded), you could create a single string literal of the maximum size, perhaps using a macro library as mentioned above. You can then get a string literal containing fewer than this maximum by starting int the middle:
#define Multi(literal, rep) \
(&(REP(MAXREP, literal))[((sizeof literal)-1)*(MAXREP-rep)])
For that to work, MAXREP must be previously #defined as a (smallish) integer constant (not a constant expression).
Here's a complete example program, using BOOST_PP_REPEAT from the Boost preprocessor library to define REP:
#include <stdio.h>
#include <stdlib.h>
#include <boost/preprocessor/repeat.hpp>
#define MAXREP 80
#define REPEATER(z, n, literal) literal
#define REP(n, literal) BOOST_PP_REPEAT(n, REPEATER, literal)
#define Multi(literal, rep) \
(&(REP(MAXREP, literal))[((sizeof literal)-1)*(MAXREP-rep)])
int main(int argc, char** argv) {
int reps = 0;
if (argc > 1) reps = atoi(argv[1]);
if (reps <= 0) reps = MAXREP;
if (reps > MAXREP) {
fprintf(stderr, "Cannot do %d repetitions; maximum is %d\n", reps, MAXREP);
exit(1);
}
for (int i = 0; i < reps; ++i) printf("%s\n", Multi("foo", i));
return 0;
}
Sample run:
$ make rep
cc -O0 -Wall -ggdb -std=c11 -D_XOPEN_SOURCE=700 -mtune=native rep.c -o rep
$ ./rep 5
foo
foofoo
foofoofoo
foofoofoofoo
Perhaps something employing a compound literal (since C99) to form the space needed?
MULTI(some_string_literal, n) is valid until the end of the block. No need to free.
#include <string.h>
char *Multi(char *dest, const char *s, unsigned n) {
size_t len = strlen(s);
char *p = dest;
while (n-- > 0) {
memcpy(p, s, len);
p += len;
}
*p = 0;
return dest;
}
// compound literal v-------------------------------v
#define MULTI(s, n) Multi( (char [(sizeof(s) - 1)*n + 1]){0} , (s), (n))
#include <stdio.h>
int main() {
char *tmp = MULTI("str", 3);
printf("<%s>\n", tmp);
printf("<%s> <%s>\n", MULTI("str", 4), MULTI("str", 5));
printf("%p\n", MULTI("str", 6));
}
Sample output
<strstrstr>
<strstrstrstr> <strstrstrstrstr>
0xffffcb80
One way to implement this is to use strncpy and calloc to copy the original string several times into a new char array
char* repeat(char* orig, size_t times) {
if (times == 0) return calloc(1, sizeof(char)); // is the empty string
else {
size_t orig_length = strlen(orig);
size_t new_length = times * orig_length;
char* new_str = malloc((new_length + 1) * sizeof(char));
for (size_t i = 0; i < times; i++) {
strncpy(&new_str[orig_length * i], orig, orig_length);
}
new_str[new_length] = 0; // setting the null-byte
return new_str;
}
}
This function return always a new string, which needs to be freed with free before the last reference to it is lost or else you will have a memory leak.
This could also be done recursive, but this won't do much for this kind of function.
And this can most likly be optimized, feel free to suggest improvements.
Yes pure C can do a lot my friend, here I have written a function for you
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * strRepeat(char *str, int n) {
int len = strlen(str);
char *repeatedStr = (char *)malloc(len * n + 1);
if(repeatedStr == NULL) {
return NULL;
}
for(int i = 0;i < len * n; i++) {
repeatedStr[i] = str[i % len];
}
repeatedStr[len * n] = '\0';
return repeatedStr;
}
int main(void) {
char *s = strRepeat("str", 7);
printf("%s", s);
//output: strstrstrstrstrstrstr
free(s);
return 0;
}
After reading this and this, I understand that
void va_start(va_list ap, last);
Saves in va_list pa some information about arguments whose number and types are not known to the called function. Later arguments can be extracted via va_arg().
Documentation doesn't specify how things are put in va_list pa
In short: I need to know how its being saved in memory. Its memory representation.
#include<stdarg.h>
#include<stdio.h>
int sum(int, ...);
int main(void)
{
printf("Sum of 10, 20 and 30 = %d\n", sum(3, 10, 20, 30) );
return 0;
}
int sum(int num_args, ...)
{
int val = 0;
va_list ap;
int i;
va_start(ap, num_args);
for(i = 0; i < num_args; i++)
{
val += va_arg(ap, int);
}
va_end(ap);
return val;
}
What information is stored in pa ? how does it look in memory for the above example ?
And how from the above example it knows the type is int and not something else ?
It is implementation-dependent. Sometimes, va_list is simply char*. Basically, ap represents an array of pointers to chars. When calling va_arg(ap, TYPE) it reads memory from address at which ap points, but it reads it as TYPE type. So, if you call it with int it will read 4 bytes and convert it to int type.
I have a function A with a body like so
char* A (const char* arg) {
char ret[8192];
B(ret);
return strdup(ret);
}
Function B looks like this (some pseudo code on iteration logic for brevity)
void B(char* ret) {
char retString[8192];
while(ITERATIONS_LEFT) {
snprintf(returnString, 8192, "\n Format %s\n\n", IT_VALUE);
snprintf(returnString, 8192, "\n Val %s\n\n", IT_VALUE_2);
}
strcpy(ret, returnString);
}
So essentially I have a function A that gives another function B a string buffer for B to enter formatted data into. Now this works fine as long as the total data returned from the iterations does not exceed 8196 (just a guess at a 'sufficiently large' value) but I think it would be better if I could do this dynamically and not have to worry about the case where my buffer fills. How would I achieve this in a fairly efficient manner, with the constraints that function A must still call function B, and that B's signature can be changed but A's cannot?
In addition to your allocation problem, you overwrite the same string here:
snprintf(returnString, 8192, "\n Format %s\n\n", IT_VALUE);
snprintf(returnString, 8192, "\n Val %s\n\n", IT_VALUE_2);
You could solve this with a kind of "appender" that re-allocates memory as it is needed by determining the required length by passing a size of 0 to snprintf as Joachim Pileborg suggested:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
struct append_t {
char *str; /* string */
size_t len; /* length of string */
size_t size; /* allocated size */
};
void append(struct append_t *app, const char *fmt, ...)
{
va_list arg;
size_t len;
va_start(arg, fmt);
len = vsnprintf(NULL, 0, fmt, arg);
va_end(arg);
while (app->len + len + 1 >= app->size) {
app->size = app->size ? app->size * 2 : 0x100;
app->str = realloc(app->str, app->size);
// Check and handle error
}
va_start(arg, fmt);
len = vsnprintf(app->str + app->len, app->size - app->len, fmt, arg);
va_end(arg);
app->len += len;
}
int main(int argc, char **argv)
{
struct append_t app = {NULL};
for (int i = 1; i < argc; i++) {
if (i > 1) append(&app, ", ");
append(&app, "'%s'", argv[i]);
}
if (app.str) puts(app.str);
free(app.str);
return 0;
}
Things to note:
The code uses the fact that realloc(NULL, size) behaves like malloc(size). The appender must be initialised to all zero.
vsnprintf is a variant of snprintf that takes a va_list instead of variadic arguments. The v...printf functions allow you to write your own printf-like functions. You can't pass variadic arguments to other functions, you have to create a va_list with the va_... macros from the <stdarg.h> header.
Most compilers can detect mismatches between printing formats and arguments for the standard printf functions. If you wat to benefit from these checks for your function, you could use the appropriate GCC attributes ((format(printf, 2, 3)) or the SAL annotation _Printf_format_string_.
In your example, A would create the appender and pass it to B and then return its .str. You could also return an appender from B and return its .strfrom A.
I suggest the following versions of A and B.
char* A (const char* arg) {
int size = 8192;
char *ret = malloc(size);
B(ret, size);
return ret;
}
void B(char* ret, int size) {
int pos = 0, required;
while(ITERATIONS_LEFT) {
required = snprintf(NULL, 0, "\n Format %s\n\n", IT_VALUE);
if (pos + required >= size) {
size *= 2;
ret = realloc(ret, size);
}
pos += sprintf(ret + pos, "\n Format %s\n\n", IT_VALUE);
required = snprintf(NULL, 0, "\n Val %s\n\n", IT_VALUE_2);
if (pos + required >= size) {
size *= 2;
ret = realloc(ret, size);
}
pos += sprintf(ret + pos, "\n Val %s\n\n", IT_VALUE_2);
}
}
Note that:
buffer size is doubled if it isn't enough. That works well in most cases.
copying is minimized (no strdup or strcpy)
you may want to make a new function with the repeated code in the while loop
In your version of B buffer is overwritten each time you call snprintf. Here the writing position (pos) is updated to append (null terminating char overwritten)
snprintf with NULL argument will return the required buffer size without printing anything anywhere
You may want to check that ret is not NULL after calling realloc
If ret is not NULL it's certain that the buffer is large enough. Thus simple sprintf is used to actually print.
Remember to free the buffer!
I haven't tested the code myself
I have a function in C, which takes a bunch of arguments, and I would like to treat those arguments like an array and access them by number. For example, say I want to take 6 arguments plus a parameter from 1 to 6, and increment the corresponding argument. I could do:
void myFunc(int arg1,int arg2,int arg3,int arg4,int arg5,int arg6,n)
{
if (n==1) ++arg1;
else if (n==2) ++arg2;
else if (n==3) ++arg3;
else if (n==4) ++arg4;
else if (n==5) ++arg5;
else if (n==6) ++arg6;
}
But that's a bit messy. Is there a neater way to do this?
Although as suggested in the comments passing a pointer to an array may be easier. If you really want to go with arguments then your best bet may be to use a variadric function:
void myFunc(int n, ...)
{
va_list ap;
int arg;
va_start(ap, n);
while (--n)
arg = va_arg(ap, int); /* Increments ap to the next argument. */
va_end(ap);
arg++;
}
I'd do it like this:
void myFunc(int n, ...)
{
va_list args;
va_start(args, n);
int temp;
for(n; n; --n)
{
temp = va_arg(vl, int);
}
temp++;
va_end(args);
}
A few things to note:
This does no handling if n == 0, and will be wrong in that case.
Because C is pass by value, this will increment the variable locally, (as your original function), but the change will NOT take effect outside the function!
You can use a temporary array of pointers to your arguments, then you can access them through this array of pointers:
void myFunc(int arg1,int arg2,int arg3,int arg4,int arg5,int arg6,n)
{
int *array_of_args[] = {&arg1, &arg2, &arg3, &arg4, &arg5, &arg6};
if (n >= 1 && n <= 6)
++*array_of_args[n - 1];
}
This is not better than your original code, but if your code uses the array-access several times, this hack will make the code smaller.
Pass your arguments in as an array. Here I just used literals, but you could replace 1,2,3,4 with your own variables like arg1, arg2, and so on.
int myNumbers[] = { 1, 2, 3, 4 };
myFunc(myNumbers, sizeof myNumbers / sizeof myNumbers[0]);
Then, your function needs to be prepared to accept the array. Also, rather than using six if's to check six arguments, we can write a for loop. However, that is entirely unrelated to the question and I understand you may be doing this for a class assignment.
void myFunc(int *args, int numArgs)
{
int i = 0;
for(i; i < numArgs; i++)
{
if(args[i] == i+1) ++args[i];
}
}
I wrote a variadic C function which mission is to allocate the needed memory for a buffer, and then sprintf the args given to this function in that buffer. But I'm seeing a strange behavior with it. It works only once. If I have two calls for this function it segfaults.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
char *xsprintf(char * fmt, ...)
{
va_list ap;
char *part;
char *buf;
size_t len = strlen(fmt)+1;
va_start(ap, fmt);
while (part = va_arg(ap, char *))
len += strlen(part);
va_end(ap);
buf = (char*) malloc(sizeof(char)*len);
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
return buf;
}
int main(int argc, const char *argv[])
{
char *b;
b = xsprintf("my favorite fruits are: %s, %s, and %s", "coffee", "C", "oranges");
printf("size de buf is %d\n", strlen(b)); //this works. After it, it segfaults.
/*
free(b);
b = NULL;
*/
b = xsprintf("my favorite fruits are: %s, %s, and %s", "coffee", "C", "oranges");
printf("size de buf is %d\n", strlen(b));
printf("%s", b);
return 0;
}
here's the output of this program:
size de buf is 46
[1] 4305 segmentation fault ./xsprintftest
Am I doing something wrong? Shouldn't I have used va_start multiple times in a single function? Do you have any alternatives? Thanks a lot! :)
You should be using vsnprintf. Use it twice. Once with a NULL destination/zero size to find out the length of the buffer you need to allocate, then a second time to fill the buffer. That way your function will work even if all the arguments are not strings.
As written, it will fail if there are any non-string arguments (%d, %x, %f, etc.). And counting the number of % characters is not a valid way to get the number of arguments. Your result could be too many (if there are literal % characters encoded as %%) or too few (if arguments are also needed for %*s, %.*d, etc. width/precision specifiers).
Pass NULL as the last arg to xsprintf():
b = xsprintf("my favorite fruits are: %s, %s, and %s",
"coffee", "C", "oranges", (void*)0);
Then your while() loop will see the NULL and terminate properly.
As R.. mentions in the comment below and in another answer, the xsprintf function will fail if there are other format arguments. You are better off using vsprintf as explained in the other answer.
My intent here was simply to demonstrate the use of a sentinel with va_arg.
First off, try using vsnprintf. It's just a good idea.
That's not your problem, though. Your problem is that you can't call va_arg more times than there are arguments. It doesn't return the number of arguments. You must either pass in a parameter telling it how many there are, or extract the number of special tokens in the format string to figure out how many there must implicitly be.
That's the reason why printf can choke your program if you pass it too few arguments; it will just keep pulling things off the stack.
The problem is that in the bit of code where you're accessing the va_arg() list without a particular defined end:
va_start(ap, fmt);
while (part = va_arg(ap, char *))
len += strlen(part);
va_end(ap);
The stdargs.h facilities don't have any built-in method for determining when the end of the va_list() occurs - you need to have that explicitly done by a convention you come up with. Either using a sentinel value (as in bstpierre's answer), or by having a count provided. A count can be an explicit parameter that's provided, or it can be implicit (such as by counting the number of format specifiers in the format string like the printf() family does).
Of course, you also have the issue that your code currently only supports the one kind of format-specifier (%s), but I assumed that that's intentional at this point.
Thanks a lot for your answers and ideas! So I rewrote my function like this:
void fatal(const char *msg)/*{{{*/
{
fprintf(stderr, "program: %s", msg);
abort ();
}/*}}}*/
void *xmalloc(size_t size)/*{{{*/
{
register void *value = malloc(size);
if (value == 0)
fatal ("Virtual memory exhausted");
return value;
}/*}}}*/
void *xrealloc(void *ptr, size_t size)/*{{{*/
{
register void *value = realloc(ptr, size);
if (value == 0)
fatal ("Virtual memory exhausted");
return value;
}/*}}}*/
char *xsprintf(const char *fmt, ...)/*{{{*/
{
/* Heavily inspired from http://perfec.to/vsprintf/pasprintf */
va_list args;
char *buf;
size_t bufsize;
char *newbuf;
size_t nextsize;
int outsize;
int FIRSTSIZE = 20;
bufsize = 0;
for (;;) {
if(bufsize == 0){
buf = (char*) xmalloc(FIRSTSIZE);
bufsize = FIRSTSIZE;
}
else{
newbuf = (char *)xrealloc(buf, nextsize);
buf = newbuf;
bufsize = nextsize;
}
va_start(args, fmt);
outsize = vsnprintf(buf, bufsize, fmt, args);
va_end(args);
if (outsize == -1) {
/* Clear indication that output was truncated, but no
* clear indication of how big buffer needs to be, so
* simply double existing buffer size for next time.
*/
nextsize = bufsize * 2;
} else if (outsize == bufsize) {
/* Output was truncated (since at least the \0 could
* not fit), but no indication of how big the buffer
* needs to be, so just double existing buffer size
* for next time.
*/
nextsize = bufsize * 2;
} else if (outsize > bufsize) {
/* Output was truncated, but we were told exactly how
* big the buffer needs to be next time. Add two chars
* to the returned size. One for the \0, and one to
* prevent ambiguity in the next case below.
*/
nextsize = outsize + 2;
} else if (outsize == bufsize - 1) {
/* This is ambiguous. May mean that the output string
* exactly fits, but on some systems the output string
* may have been trucated. We can't tell.
* Just double the buffer size for next time.
*/
nextsize = bufsize * 2;
} else {
/* Output was not truncated */
break;
}
}
return buf;
}/*}}}*/
And it's working like a charm! Thanks a million times :)