I'm writing a function to stringify a nested struct. I'm using snprintf
to pre-calculate how much space I'll need to allocate.
The problem is, it's extremely repetitious, and will only get worse if I ever need to add more fields to any of the structs. The duplication also creates more of an opportunity for accidental asymmetrical changes.
Is there an idiomatic way of reducing the duplication?
The repetitious code:
char* stringify_state(State* state) {
Pet* p = state->pet;
Settings* s = state->settings;
char* format_string = "%lld %f %f %f %f %f %f %f %f %f %f";
size_t len = snprintf(NULL, 0, format_string,
state->last_update_time,
p->health, p->max_health, p->satiation, p->max_satiation,
s->milliseconds_per_tick, s->hunger_pain_per_tick, s->hunger_per_tick, s->satiated_heal_per_tick,
s->pain_per_wrong_answer, s->satiation_per_right_answer);
char* buff = malloc(len + 1);
if (buff) {
snprintf(buff, len + 1, format_string,
state->last_update_time,
p->health, p->max_health, p->satiation, p->max_satiation,
s->milliseconds_per_tick, s->hunger_pain_per_tick, s->hunger_per_tick, s->satiated_heal_per_tick,
s->pain_per_wrong_answer, s->satiation_per_right_answer);
return buff;
} else {
return NULL;
}
}
Context (Not what I'm asking about, but feel free to mention anything):
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct {
double health;
double satiation;
double max_health;
double max_satiation;
} Pet;
void init_pet(Pet* pet, double max_health, double max_satiation) {
pet->max_health = max_health;
pet->max_satiation = max_satiation;
pet->health = max_health;
pet->satiation = max_satiation;
typedef struct {
double milliseconds_per_tick;
double hunger_pain_per_tick;
double hunger_per_tick;
double satiated_heal_per_tick;
double pain_per_wrong_answer;
double satiation_per_right_answer;
} Settings;
void init_settings(Settings* s,
double ms_per_tick,
double hung_pain_per_tick, double hung_per_tick,
double sat_heal_per_tick,
double pain_per_wrong, double sat_per_right) {
s->milliseconds_per_tick = ms_per_tick;
s->hunger_pain_per_tick = hung_pain_per_tick;
s->hunger_per_tick = hung_per_tick;
s->satiated_heal_per_tick = sat_heal_per_tick;
s->pain_per_wrong_answer = pain_per_wrong;
s->satiation_per_right_answer = sat_per_right;
}
typedef struct {
time_t last_update_time;
Pet* pet;
Settings* settings;
} State;
void init_state(State* state, Pet* pet, Settings* settings, time_t t) {
state->pet = pet;
state->settings = settings;
state->last_update_time = t;
}
A test main:
int main() {
Pet pet;
init_pet(&pet, 100, 100);
Settings settings;
init_settings(&settings, 91, 82, 73, 64, 55, 46);
State state;
init_state(&state, &pet, &settings, 0);
char* str = stringify_state(&state);
if (str) {
printf("%s", str);
free(str);
}
}
Prints
0 100.000000 100.000000 100.000000 100.000000 91.000000 82.000000 73.000000 64.000000 55.000000 46.000000