Reputation: 43
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
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
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