I have various array content "templates" of type uint8_t
that I'd like to define in a special header file. Those content templates also have different lengths:
#define CONTENT_VARIANT_A { 5, 3, 8, 1, 4, 23 }
#define CONTENT_VARIANT_B { 1, 10, 2 }
#define CONTENT_VARIANT_C { 4, 39, 2, 39 }
// '0' is not a valid element value (=> can be used for loop termination)
#define CONTENT_MAX_SIZE = 20;
In my code, I'd like to have a method to set the content of an array buffer to one of those pre-defined values. This is my code so far, using switch
and memcpy
:
Method to set the content:
void SetBuffer(uint8_t *my_buffer, uint8_t chosen_content) {
memset(my_buffer, 0, CONTENT_MAX_SIZE);
switch (chosen_content) {
case CHOICE_VARIANT_A: {
uint8_t new_content[] = CONTENT_VARIANT_A;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
case CHOICE_VARIANT_B: {
uint8_t new_content[] = CONTENT_VARIANT_B;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
case CHOICE_VARIANT_C: {
uint8_t new_content[] = CONTENT_VARIANT_C;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
}
}
Usage:
// Buffer declaration (done once)
uint8_t my_buffer[CONTENT_MAX_SIZE] = { 0 };
// Buffer population + usage (executed multiple times, with varying values for 'chosen_content')
SetBuffer(my_buffer, chosen_content);
uint8_t i = 0;
while (i < CONTENT_MAX_SIZE && my_buffer[i] > 0) {
// ...
++i;
}
I'm a C# programmer, and new to C; the code in SetBuffer
seems overly complicated to me, but is the only thing my mind could come up with that should work (with regards to what I think I know about C), and that also compiles. Is it the correct way of doing what I want, or is it pell-mell and should be done completely different?
In case the zero-out isn't necessary, you can shave the function down to something like this:
void SetBuffer(uint8_t *my_buffer, uint8_t chosen_content) {
switch (chosen_content) {
case CHOICE_VARIANT_A: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_A, sizeof((uint8_t[])CONTENT_VARIANT_A)); break;
case CHOICE_VARIANT_B: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_B, sizeof((uint8_t[])CONTENT_VARIANT_B)); break;
case CHOICE_VARIANT_C: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_C, sizeof((uint8_t[])CONTENT_VARIANT_C)); break;
}
}
Where (uint8_t[])CONTENT_VARIANT_A
is not a cast but together with the macro forms a compound literal. Essentially a local, anonymous temporary array. The sizeof
expression is similar and calculated at compile-time.
If you must zero-out non-used cells, then replace (uint8_t[])
with (uint8_t[CONTENT_MAX_SIZE])
. C guarantees that items not contained in the initializer list gets set to zero.
Yet another alternative for speed over readability is an evil macro:
#define SetBuffer(my_buffer, content) \
memcpy(my_buffer, \
(uint8_t[])CONTENT_VARIANT_##content, \
(uint8_t[])CONTENT_VARIANT_##content)
Call as SetBuffer(buf, A);
etc. It's fairly type safe since unknown letter prefixes will result in compiler errors. You might also want to ask yourself why you aren't simply using memcpy
on the caller side.