4

Messing around with / learning C. I want a one-liner for moving stack values into the heap and this is the best I could come up with.

#define defheapify(nm, T, sz) \
  T* heapify_##nm (const T x) { \
    T *nx = malloc(sz); \
    nx[0] = x; \
    return nx; \
  }

defheapify(char, char, sizeof(char));
defheapify(uchar, unsigned char, sizeof(unsigned char));
defheapify(short_int, short int, sizeof(short int));
defheapify(ushort_int, unsigned short int, sizeof(unsigned short int));
defheapify(int, int, sizeof(int));
defheapify(uint, unsigned int, sizeof(unsigned int));
defheapify(long_int, long int, sizeof(long int));
defheapify(ulong_int, unsigned long int, sizeof(unsigned long int));
defheapify(long_long_int, long long int, sizeof(long long int));
defheapify(ulong_long_int, unsigned long long int, sizeof(unsigned long long int));
defheapify(float, float, sizeof(float));
defheapify(double, double, sizeof(double));
defheapify(long_double, long double, sizeof(long double));

It seems to work:

  short int *si = heapify_short_int(20);
  printf("%d\n", ((int*)si)[0]); /* => 20 */

Is there a better way to accomplish this?

jxh
  • 69,070
  • 8
  • 110
  • 193
crinklywrappr
  • 588
  • 5
  • 18
  • 8
    why would you want to do this? – Christian Gibbons Dec 30 '19 at 21:18
  • Besides maybe having `*nx = x` instead of using `[0]` and adding a check to make sure `malloc` doesn't return `NULL` my only though is why. – kopecs Dec 30 '19 at 21:19
  • 2
    Excellent way to fragment your heap. – Eugene Sh. Dec 30 '19 at 21:20
  • @EugeneSh. That happens whether you do it by defining functions for all your types, or write out the malloc code in each place. – Barmar Dec 30 '19 at 21:22
  • @Christian Gibbons, My first thought is that they want an initializing allocator, but the initialization only works for simple types. So yeah, why would one dynamically dynamically allocate simple values? – ikegami Dec 30 '19 at 21:23
  • "Is there a better way to accomplish this?" --> Handle `heapify_...(0)` and cope with out-of-memory, else this is not much different than VLA which fails to handle those two. – chux - Reinstate Monica Dec 31 '19 at 14:31

1 Answers1

6

Since this is C:

void * heapify (const void *p, size_t sz) {
    void *x = malloc(sz);
    if (x) memcpy(x, p, sz);
    return x;
}

Then, if you insist:

#define defheapify(nm, T, sz) \
T* heapify_##nm (const T x) { return heapify(&x, sz); }

But, sz is redundant if you have T, so:

#define defheapify(nm, T) \
T* heapify_##nm (const T x) { return heapify(&x, sizeof(x)); }

However, if you are only ever concerned about the types you listed in your question, you can use a _Generic switch instead and simplify your interface. With the code below, you can remove the task of figuring out the type of the variable or constant you are dealing with. Just always call heapify_any.

#define heapify_T(T, X) \
(T *)heapify(&(struct{ T x; }){X}.x, sizeof(T))

#define heapify_G(T, X) T:heapify_T(T, X)

#define heapify_any(X) _Generic( \
X, \
heapify_G(_Bool, X), \
heapify_G(char, X), \
heapify_G(signed char, X), \
heapify_G(unsigned char, X), \
heapify_G(short int, X), \
heapify_G(unsigned short int, X), \
heapify_G(int, X), \
heapify_G(unsigned int, X), \
heapify_G(long int, X), \
heapify_G(unsigned long int, X), \
heapify_G(long long int, X), \
heapify_G(unsigned long long int, X), \
heapify_G(float, X), \
heapify_G(double, X), \
heapify_G(long double, X), \
default:(void)0 \
)

There is no such thing as a literal short (or char or _Bool) value in C, so you would need a variable or a cast to use the heapify_any macro above.

short int *si = heapify_any((short)20);
printf("%hd\n", *si);

Try it online!

jxh
  • 69,070
  • 8
  • 110
  • 193
  • 1
    I'm fairly certain the C preprocessor restricts `sizeof`. – crinklywrappr Dec 30 '19 at 21:39
  • 1
    @crinklywrappr What do you mean? – Eugene Sh. Dec 30 '19 at 21:40
  • I ran across this about an hour ago. I didn't test it so I don't know if it's true or not. Still learning :-) https://stackoverflow.com/a/4079267/787747 – crinklywrappr Dec 30 '19 at 21:44
  • 1
    This is not the case here. We do not ask the preprocessor to evaluate `sizeof`, we just ask it to place it as is where the macro is being expanded. – Eugene Sh. Dec 30 '19 at 21:45
  • I am enlightened. Thanks! (= – crinklywrappr Dec 30 '19 at 21:47
  • @crinklywrappr You are confusing with this kind of preprocessing directives: `#if sizeof(int) == 4`. This directive is not valid because the preprocessor does not understand the `sizeof` operator (so, it cannot evaluate it). But this is not the case here. What at issue here is a textual replacement only . – Lxer Lx Dec 30 '19 at 22:07
  • 1
    The "_Bool users legendary league society hotline integration team" (b.u.l.l.s.h.i.t.) wishes to lodge a complaint that `_Bool` was not on the `_Generic` list. Their motto: "Don't step on the `_Bool`! – chux - Reinstate Monica Dec 31 '19 at 00:38
  • @chux-ReinstateMonica: Noted, thanks! I suppose I will have to add wide character support too? – jxh Dec 31 '19 at 08:10
  • 1
    I do not think `wchar_t` (and others) are intrinsic types. "`wchar_t`, an integer type defined in the `` header". May collided with others. – chux - Reinstate Monica Dec 31 '19 at 14:26