Conditionally initialize an array

Viewed 154

I want an array to have two different values, based on a condition. I can initialize the array inside the condition with the values i want.

if (myCondition == 0)
{
  byte my_message[8] = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
}
else if (myCondition == 1)
{
  byte my_message[8] = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
}

The problem with the previous approach is that the array has local scope, and it cannot be "seen" by the code beneath.

If I try to declare the array outside the condition with:

byte my_message[8];

then, inside the condition, I cannot use the previous way of initializing the whole array all at once.

There is no pattern in the data so I can use a for loop- inside the condition- in order to give value to each element of the array easily.

Is there a way of giving values to the array, besides the cumbersome:

if (myCondition == 0)
{
  my_message[0] = {0x00}; 
  my_message[1] = {0xAB};
  my_message[2] = {0xEE};
  ....
}
6 Answers

In C you can use standard function memcpy and compound literals the following way

byte my_message[8];
if (myCondition == 0)
{
  memcpy( my_message, ( byte [] ){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B}, 8 * sizeof( byte ));
}
else if (myCondition == 1)
{
  memcpy( my_message, ( byte[] ){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}, 8 * sizeof( byte ) );
}

In C++ it is better to use std::array instead of a raw array. For example

std::array<byte, 8>  my_message;
if (myCondition == 0)
{
  my_message = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
}
else if (myCondition == 1)
{
  my_message = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
}

Or if to use a raw array then if the compiler supports C++ 20 then you can use the range based for loop the following way

byte my_message[8];

if (myCondition == 0)
{
  for ( auto *p = my_message; auto item : {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B} )
  {
      *p++ = item;
  }
}
else if (myCondition == 1)
{
  for ( auto *p = my_message; auto item : {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10} )
  {
      *p++ = item;
  }
}

Otherwise if the compiler does not support C++ 20 then this declaration

auto *p = my_message;

must be moved outside the range based for loop.

If my_message isn't changed, you could use a pointer instead of an array.

const byte my_messages[2][8] = {
   { 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B },
   { 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 },
};

const byte *my_message = my_messages[ myCondition ];

If you need to be able to change my_array, I'd use the following:

const byte my_messages[2][8] = {
   { 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B },
   { 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 },
};

byte my_message[8];
memcpy( my_message, my_messages[ myCondition ], sizeof( my_message ) );

You could memcpy from an anonymous array, but it's overly complicated and involves code repetition:

byte my_message[8];
if ( myCondition ) {
   memcpy( my_message, ( byte[] ){ 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 }, sizeof( my_message) );
} else {
   memcpy( my_message, ( byte[] ){ 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B }, sizeof( my_message) );
}
  1. You can wrap the message into the struct and use assign operator with compound literal.
  2. You can use compound literal + memcpy
typedef unsigned char byte;

typedef struct 
{
    byte my_message[8];
}message_t;

int foo(int myCondition)
{
    message_t my_message;
    if (myCondition == 0)
    {
        my_message = (message_t){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
    }
    else if (myCondition == 1)
    {
        my_message = (message_t){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
    }
    return 0;
}

int bar(int myCondition)
{
    byte my_message[8];
    if (myCondition == 0)
    {
        memcpy(my_message, (byte[]){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B}, sizeof(my_message));
    }
    else if (myCondition == 1)
    {
        memcpy(my_message, (byte[]){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}, sizeof(my_message));
    }
    return 0;
}

or you can myCondition as index:

int zoo(int myCondition)
{
    byte my_message[8];
    memcpy(my_message, 
          (byte[][8]){{0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B},
                      {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}}[myCondition], 
          sizeof(my_message));
    return 0;
}

memcpy way looks the most efficient:

https://godbolt.org/z/d6bMfejz5

Surprisingly, this effect is achievable using a reference to an array in C++. I'm not sure how compliant this solution is but it compiles in pedantic mode for CLANG, GCC, ICC and MSVC.

int foo(bool cond) {
    byte arr0[8] = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
    byte arr1[8] = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
    byte (&tab)[8] = cond ? arr0 : arr1;
    return tab[0];
}

See https://godbolt.org/z/P6Th7bfcn

Variadic template variant:

#include <array>
#include <cstdint>
#include <cstdlib>
#include <iostream>

using byte = unsigned char;

template <typename T, typename... Ts>
std::array<T, sizeof...(Ts)> make_array(Ts... ts) {
    
    return std::array<T, sizeof...(Ts)> { (T)ts... };
}

int main()
{
    bool myCondition = false;
    std::array<byte, 8> ary = myCondition 
        ? make_array<byte>(7,6,5,4,3,2,1,0) 
        : make_array<byte>(0,1,2,3,4,5,6,7);

    for(auto it=ary.begin(); it != ary.end(); ++it)
    {
            std::cout << (int)*it << std::endl;
    }
    
    return 0;
}

Why "design-in" a boolean limitation of only two conditions?

The following allows for what could be easily be less code in main(), and up to four conditions... (I've altered the values of the 4x8 bytes to bury an Easter Egg for those who are curious. Happy hunting...)

#include <stdio.h>

void show( byte *m ) { printf( "%.8s\n", m ); }

int main() {
    const byte my_messages[][8] = {
        { 0x41, 0x4C, 0x4c, 0x20, 0x47, 0x4F, 0x4F, 0x44 },
        { 0x48, 0x41, 0x54, 0x45, 0x4D, 0x41, 0x52, 0x53 },
        { 0x53, 0x45, 0x4E, 0x44, 0x48, 0x45, 0x4C, 0x50 },
        { 0x54, 0x4F, 0x4F, 0x20, 0x4C, 0x41, 0x54, 0x45 },
    };
    byte msg[8];

#   define FETCH( m, cond ) do { memcpy( m, my_messages[cond], sizeof m ); } while(0)

    FETCH( msg, 0 );    show( msg ); // prove

    FETCH( msg, 1 );    show( msg ); // prove

    FETCH( msg, 2 );    show( msg ); // prove

    FETCH( msg, 3 );    show( msg ); // prove

    return 0;
}
Related