Home > Enterprise >  Is there a clean and short syntax to allocate and fill values for a struct pointer (that is not dest
Is there a clean and short syntax to allocate and fill values for a struct pointer (that is not dest

Time:01-24

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ALLOC3(t, n, a, b, c) t* n = malloc(sizeof(t));\
memcpy(n, &((t){a, b, c}), sizeof(t));

int wickCount = 3;
char* color = "green";
char* scent = "Fresh Balsam";

typedef struct Candle_ {
  int wickCount;
  char* color;
  char* scent;
} Candle;

Candle* maker1 () {
  /* Imagine some code here that generates data,
  but for now, I will use hard-coded values */
  
  /* Create candle */
  Candle* candle = malloc(sizeof(Candle));
  candle->wickCount = wickCount;
  candle->color = color;
  candle->scent = scent;
  return candle;
}

Candle* maker2 () {
  Candle* candle = malloc(sizeof(Candle));
  memcpy(candle, &((Candle){wickCount, color, scent}), sizeof(Candle));
  return candle;
}

Candle* maker3 () {
  Candle* candle = malloc(sizeof(Candle));
  *candle = (Candle){wickCount, color, scent};
  return candle;
}

Candle* maker4 () {
  ALLOC3(Candle, candle, wickCount, color, scent);
  return candle;
}

int main () {
  Candle* candle = maker4();
  /* Do some random things to check for memory leaks.
  Let me know if there is a better way to do this. */
  int temp = 5;
  char* tempstr = "Some filler text...";
  printf("Filler text...\n");
  printf("%s\n", tempstr);
  printf("Filler text...\n");
  printf("==============\n");
  printf("Values:\n");
  /* Finally, print the values */
  printf("%d\n", candle->wickCount);
  printf("%s\n", candle->color);
  printf("%s\n", candle->scent);
  return 0;
}

Maker1 works, but you have to set every member one by one by name.

Maker2 works, but still needs 2 lines and extra syntax.

Maker3 seems to work here and is less words, but it seems to cause memory leaks sometimes.

Maker4 also works, but I would have to make macros for all numbers of members, like ALLOC1, ALLOC2, ALLOC3, ALLOC4, and so on. Surely there is a less hacked together way than macros.

There is a nice syntax for the global scope:

Candle candle = {wickCount, color, scent};

Is there any way to allocate and set all members in a short syntax from inside a function without the values becoming destroyed when the function ends?

UPDATE I figured out a macro that works for any number of members:

#define ALLOC(s, v, p) s* p = malloc(sizeof(s));\
memcpy(p, &v, sizeof(s));

/* Then you can call it with this: */
ALLOC(Candle, ((Candle){wickCount, color, scent}), candle);

But it is still not the cleanest and I would still not like to use a macro if there is a better build-in standard way.

CodePudding user response:

You can put your whole code in a macro and call it "short" then. You might want to learn about __VA_ARGS__ and variadic macros. Your code does not handle allocation errors.

#define ALLOCRET(TYPE, ...) \
   TYPE *_tmp = malloc(sizeof(TYPE)); \
   if (_tmp) \
       *_tmp = (TYPE){ __VA_ARGS__ }; \
   return _tmp;

Candle *maker5(void) {
   ALLOCRET(Candle, wickCount, color, scent);
}

Subjective: Overall, maker3 is the most readable, thus would be preferred. Hiding stuff behind macro may make your code hard to maintain and unreadable. Mixing uppercase and lowercase similar names is confusing - Candle and candle. Consider using visually clearly distinct names to reduce bugs. Consider writing struct keyword when using a structure.

CodePudding user response:

Personally I would go mark3 but if you really love one-liners you could try the following macro:

#define DYNINIT(T, ...) ((T*)memcpy(malloc(sizeof(T)), &(T){ __VA_ARGS__ }, sizeof (T)))
...

Candle *maker5(void) {
   return DYNINIT(Candle, wickCount, color, scent);
}

Or use it directly in main(), e.g.

Candle* candle = DYNINIT(Candle, .wick = 2, .color="brown");

The macro does following steps:

  1. Allocate memory for type T with malloc(sizeof(T))
  2. Create a compound literal with an initializer take from variadic macro
(T){ __VA_ARGS__)

Passing macro parameters as the initializer solves problem of making a dedicated ALLOC-like macro for each type.

  1. copy compound literal to destination memory
memcpy(malloc(...), &(T){ ... }, sizeof (T))

Note that memcpy() returns the destination address

  1. Cast the void* returned from memcpy to pointer to T to ensure some type safety.
  •  Tags:  
  • Related