Tajen
Tajen

Reputation: 43

C Macro to convert a string to a pascal string type

I would like some ideas for a macro to convert a preprocessor defined string to a pascal type string and then be able to use the macro to initialize const char arrays and the like.

Something like this would be great:

#define P_STRING_CONV(str) ...???...

const char *string = P_STRING_CONV("some string");

struct
{
    char str[30];
    ...
}some_struct = {.str = P_STRING_CONV("some_other_string")};

I already tried something like this:

#define DEFINE_PASCAL_STRING(var, str, strlen) struct {uint8_t len; char content[strlen-1];} (var) = {sizeof(str)-1, (str)}

(The strlen parameter could be removed, but I need a defined size.)

That works fine, but cannot be used to initialize elements in a struct. And for const char arrays I need to cast it to some other variable.

Any great ideas?

Upvotes: 1

Views: 773

Answers (2)

chux
chux

Reputation: 154335

to convert a string to a pascal string type

To convert a string literal, _Generic and compound literal will get close to OP objective.

For a better solution, more details and example use cases would help illustrate OP's goal.

#define P_STRING_CONV(X) _Generic((X)+0, \
  char *: &((struct {char len; char s[sizeof(X)-1]; }){ (char)(sizeof(X)-1), (X) }).len \
  )

void dump(const char *s) {
  unsigned length = (unsigned char) *s++;
  printf("L:%u \"", length);
  while (length--) {
    printf("%c", *s++);
  }
  printf("\"\n");
}

int main(void) {
  dump(P_STRING_CONV(""));
  dump(P_STRING_CONV("A"));
  dump(P_STRING_CONV("AB"));
  dump(P_STRING_CONV("ABC"));
  return 0;
}

Output

L:0 ""
L:1 "A"
L:2 "AB"
L:3 "ABC"

@Jonathan Leffler recommended that the created pascal-like string also contain a terminating null character. To do so with above code, simple change sizeof(X)-1 into sizeof(X). Then by accessing the pascal_like_string + 1, code has a pointer to a valid C string.


(X)+0 converts an array type to a pointer

sizeof(X)-!!sizeof(X) produces a size of the string literal, not counting its \0. At least 1.

struct {char len; char s[sizeof(X)-!!sizeof(X)]; } Is a right-sized pascal-like structure.

(struct {char len; char s[sizeof(X)-!!sizeof(X)]; }){ (char)(sizeof(X)-1), (X) } is a compound literal.


The following will convert a C string to a pascal like string. Note that as a pascal like string, there is no '\0'.

#include <limits.h>
#include <stdlib.h>
#include <string.h>
char *pstring_convert(char *s) {
  size_t len = strlen(s);
  assert(len <= UCHAR_MAX);
  memmove(s+1, s, len);
  s[0] = (char) (unsigned char) len;
  return s;
}

Upvotes: 2

Ian Abbott
Ian Abbott

Reputation: 17438

You could split the macro into two:

#define PASCAL_STRING_TYPE(size) struct { unsigned char len; char content[(size) - 1]; }
#define PASCAL_STRING_INIT(str) { .len = sizeof(str) - 1, .content = (str) }

Then use it like so:

static const PASCAL_STRING_TYPE(100) foo = PASCAL_STRING_INIT("foo");

struct bar {
   int answer;
   PASCAL_STRING_TYPE(100) question;
};
static const struct bar quux = {
    .answer = 42,
    .question = PASCAL_STRING_INIT("The Answer")
};

(Not tested.)

Upvotes: 0

Related Questions