From https://natecraun.net/articles/struct-iteration-through-abuse-of-the-c-preprocessor.html
Last active
January 17, 2024 11:17
-
-
Save xor-gate/e627f3578444688573a9f2d719deec93 to your computer and use it in GitHub Desktop.
ini-parser-xmacros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ConfigFormat.h" | |
#include <string.h> | |
const struct ConfigFormatField *ConfigFormatGetFieldByName(const struct ConfigFormat *Format, const char *Name, void *Structure) | |
{ | |
static struct ConfigFormatField Field; | |
for (size_t n = 0; n < Format->num_members; n++) { | |
if (strcmp(Name, Format->names[n]) != 0) { | |
continue; | |
} | |
Field.offset = Format->offsets[n]; | |
Field.size = Format->sizes[n]; | |
Field.type = Format->types[n]; | |
Field.name = Format->names[n]; | |
Field.value = Structure+Field.offset; | |
return &Field; | |
} | |
return NULL; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CONFIG_FORMAT_H__ | |
#define CONFIG_FORMAT_H__ | |
#include <stdlib.h> | |
enum ConfigFormatFieldTypes | |
{ | |
CONFIG_FORMAT_FIELD_TYPE_U32, | |
CONFIG_FORMAT_FIELD_TYPE_STR | |
}; | |
struct ConfigFormat { | |
char const *struct_name; | |
size_t num_members; | |
size_t struct_size; | |
size_t packed_size; | |
size_t *offsets; | |
size_t *sizes; | |
enum ConfigFormatFieldTypes *types; | |
char const **names; | |
}; | |
struct ConfigFormatField { | |
size_t offset; | |
size_t size; | |
enum ConfigFormatFieldTypes type; | |
const char *name; | |
void *value; | |
}; | |
const struct ConfigFormatField *ConfigFormatGetFieldByName(const struct ConfigFormat *Format, const char *Name, void *Structure); | |
#endif // CONFIG_FORMAT_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Original from https://natecraun.net/articles/struct-iteration-through-abuse-of-the-c-preprocessor.html | |
#include <stdint.h> | |
#include "ConfigFormat.h" | |
/* Error Checking */ | |
#ifndef CONFIG_STRUCT_NAME | |
#error "Did not define CONFIG_STRUCT_NAME before including ConfigFormat.h" | |
#endif | |
#ifndef CONFIG_STRUCT_FIELDS | |
#error "Did not define CONFIG_STRUCT_FIELDS before including ConfigFormat.h" | |
#endif | |
#define STR_NOEXPAND(A) #A | |
#define STR(A) STR_NOEXPAND(A) | |
#define CAT_NOEXPAND(A, B) A ## B | |
#define CAT(A, B) CAT_NOEXPAND(A, B) | |
struct CONFIG_STRUCT_NAME { | |
#define X(L, R) L R; | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
}; | |
static const struct ConfigFormat CAT(CONFIG_STRUCT_NAME, Format) = { | |
.struct_name = STR(CONFIG_STRUCT_NAME), | |
.num_members = ( | |
#define X(L, R) 1 + | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
0), | |
.struct_size = sizeof(struct CONFIG_STRUCT_NAME), | |
.packed_size = ( | |
#define X(L, R) sizeof(L) + | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
0), | |
.offsets = (size_t[]){ | |
#define X(L, R) offsetof(struct CONFIG_STRUCT_NAME, R), | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
}, | |
.sizes = (size_t []){ | |
#define X(L, R) sizeof(L), | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
}, | |
.types = (enum ConfigFormatFieldTypes []){ | |
#define X(L, R) CONFIG_FORMAT_FIELD_TYPE_U32, | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
}, | |
.names = (char const *[]){ | |
#define X(L, R) #R, | |
CONFIG_STRUCT_FIELDS | |
#undef X | |
}, | |
}; | |
#undef CONFIG_STRUCT_FIELDS | |
#undef CONFIG_STRUCT_NAME | |
#undef STR_NOEXPAND | |
#undef STR | |
#undef CAT_NOEXPAND | |
#undef CAT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ConfigIni.h" | |
#include "ConfigFormat.h" | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define CONFIG_INI_MAX_KEY_LEN 64 | |
#define CONFIG_INI_MAX_VALUE_LEN 128 | |
static void ConfigIniParseKeyValue(void *Structure, const struct ConfigFormat *Format, const char *Key, const char *Value) | |
{ | |
const struct ConfigFormatField *Field = ConfigFormatGetFieldByName(Format, Key, Structure); | |
printf("Parsed key=\"%s\", value=\"%s\"\n", Key, Value); | |
if (!Field) { | |
return; | |
} | |
printf("Found matching field in struct\n"); | |
} | |
void ConfigIniParse(void *Structure, const struct ConfigFormat *Format, const char *Data) | |
{ | |
const char *Line = Data; | |
while (*Line) { | |
char Key[CONFIG_INI_MAX_KEY_LEN]; | |
char Value[CONFIG_INI_MAX_VALUE_LEN]; | |
size_t i = 0; | |
// Parse Key | |
while (*Line && *Line != '=') { | |
if (i < CONFIG_INI_MAX_KEY_LEN - 1) { | |
Key[i++] = *Line; | |
} | |
Line++; | |
} | |
Key[i] = '\0'; | |
if (*Line == '=') { | |
i = 0; | |
Line++; // Move past '=' | |
// Parse Value | |
while (*Line && *Line != '\n') { | |
if (i < CONFIG_INI_MAX_VALUE_LEN - 1) { | |
Value[i++] = *Line; | |
} | |
Line++; | |
} | |
Value[i] = '\0'; | |
ConfigIniParseKeyValue(Structure, Format, Key, Value); | |
} | |
// Move to the next Line | |
while (*Line && *Line != '\n') { | |
Line++; | |
} | |
if (*Line == '\n') { | |
Line++; // Move past '\n' | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CONFIG_INI_H__ | |
#define CONFIG_INI_H__ | |
#include "ConfigFormat.h" | |
void ConfigIniParse(void *Structure, const struct ConfigFormat *Format, const char *Data); | |
#endif // CONFIG_INI_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CONFIG_STRUCTS_H__ | |
#define CONFIG_STRUCTS_H__ | |
#define CONFIG_STRUCT_NAME ConfigCamera | |
#define CONFIG_STRUCT_FIELDS \ | |
X(uint32_t, Version) | |
#include "ConfigFormatGen.h" | |
#endif // CONFIG_STRUCTS_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stddef.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "ConfigFormat.h" | |
#include "ConfigStructs.h" | |
#include "ConfigIni.h" | |
void | |
print_buffer(unsigned char *buffer, size_t size) | |
{ | |
for (size_t j = 0; j < size; j++) { | |
printf(" %02x", buffer[j]); | |
} | |
} | |
size_t | |
struct_pack(struct ConfigFormat *fmt, void *structure, unsigned char *buffer) | |
{ | |
size_t pos = 0; | |
for (size_t i = 0; i < fmt->num_members; i++) { | |
memcpy(buffer+pos, ((unsigned char*)structure)+fmt->offsets[i], fmt->sizes[i]); | |
pos += fmt->sizes[i]; | |
} | |
return pos; | |
} | |
size_t | |
struct_unpack(struct ConfigFormat *fmt, unsigned char *buffer, void *structure) | |
{ | |
size_t pos = 0; | |
for (size_t i = 0; i < fmt->num_members; i++) { | |
memcpy(((unsigned char*)structure)+fmt->offsets[i], buffer+pos, fmt->sizes[i]); | |
pos += fmt->sizes[i]; | |
} | |
return pos; | |
} | |
void | |
struct_print(struct ConfigFormat *fmt, void *structure) | |
{ | |
printf("%s:\n", fmt->struct_name); | |
for (size_t i = 0; i < fmt->num_members; i++) { | |
printf("\t%s: %zu %zu =", fmt->names[i], fmt->offsets[i], fmt->sizes[i]); | |
print_buffer(((unsigned char*)structure)+fmt->offsets[i], fmt->sizes[i]); | |
printf("\n"); | |
} | |
} | |
int | |
main(void) | |
{ | |
const char *Data = "Version=2"; | |
struct ConfigCamera cc = {.Version = 1}; | |
struct_print(&ConfigCameraFormat, &cc); | |
ConfigIniParse(&cc, &ConfigCameraFormat, Data); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment