Koz Ross
Koz Ross

Reputation: 3140

Returning formatted string from C function

I'm trying to write a recursive function for printing a tree-like structure. What I want to do is roughly as follows. Assume form is a function that takes the same arguments as printf, but returns the formatted string instead of printing it; Node is the struct that the structure is built from, with a val slot storing an unsigned long.

char* vals(Node* u) {
    if (leaf(u)) {
        return form("%lu", u->val);
    } else {
        return form("%s, %s", vals(u->left), vals(u->right);
    }
}

I'm not sure how to write this, as I've not been able to find a function similar to form.

Upvotes: 1

Views: 2106

Answers (3)

rici
rici

Reputation: 241761

As it happens, I have an implementation (two implementations, actually) of that function here at line 77 or so. It's just a simple wrapper designed to make calling the function easier; there are more efficient solutions but it doesn't usually make much difference.

/* Like GNU asprintf, but returns the resulting string buffer;
 * it is the responsibility of the caller to freee the buffer
 */
char* concatf(const char* fmt, ...);

The first one relies on vasprintf, which is a non-standard function which is part of Gnu glibc:

char* concatf(const char* fmt, ...) {
  va_list args;
  char* buf = NULL;
  va_start(args, fmt);
  int n = vasprintf(&buf, fmt, args);
  va_end(args);
  if (n < 0) { free(buf); buf = NULL; }
  return buf;
}

The second one relies on vsnprintf, which is a Posix-standard interface but was incorrectly implemented in some older standard C library implementations (glibc until version 2.0.6, for example.)

char* concatf(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  char* buf = NULL;
  int n = vsnprintf(NULL, 0, fmt, args);
  va_end(args);
  if (n >= 0) {
    va_start(args, fmt);
    buf = malloc(n+1);
    if (buf) vsnprintf(buf, n+1, fmt, args);
    va_end(args);
  }
  return buf;
}

Feel free to use them as you need them. They're not particularly profound.

For gcc (and, I think, clang), you can declare the function as:

char* concatf(const char *fmt, ...)
      __attribute__ ((format (printf, 1, 2)));

which will enable the compiler to check the validity of the format string, as it does with printf

Upvotes: 3

R Sahu
R Sahu

Reputation: 206607

I would suggest creating two form functions, one for the leaf nodes and the other for the non-leaf nodes.

char* formLeaf(Node* u) {
    char leafRet[100];
    sprintf(leafRet,"%lu", u->val); 
    return strdup(leafRet);
}

char* vals(Node* u);

char* formNonLeaf(Node* u) {
    char* left = vals(u->left);
    char* right = vals(u->left);
    char* ret = malloc(strlen(left) + strlen(right) + 3);
    sprintf(ret, "%s, %s", left, right);
    free(left);
    free(right);
    return ret;
}

char* vals(Node* u) {
    if (leaf(u)) {
        return formLeaf(u);
    } else {
        return formNonLeaf(u);
    }
}

Upvotes: 0

jcaron
jcaron

Reputation: 17710

You're probably looking for asprintf? Note that you should also free previously received allocated strings once you have used them and they are no longer needed (i.e. between the asprintf that uses them and the return, so you'll need an intermediate variable).

Upvotes: 1

Related Questions