1

How to elegantly simplify (or eliminate) the for loops in the code below, using C++17 features ?

#pragma pack(push, 1)
        typedef struct ethernet_frame
        {
            unsigned char   dst[6]; 
            unsigned char   src[6]; 
            unsigned short  proto;
        } ethernet_frame, *ethernet_frame_ptr;
#pragma pack(pop)

        int i;
        ethernet_frame eFrame = { {00,00,00,00,00,00}, {42, 54, 33, 67, 14, 88}, 0x800 };
        ProcessFrame(&eFrame); //A library function expecting an address of an ethernet_frame with its strict bit layout

        i = 0;
        for (unsigned char c : { 36, 84, 23, 77, 35, 11 }) eFrame.dst[i++] = c;
        ProcessFrame(&eFrame);

        i = 0;
        for (unsigned char c : { 65, 23, 74, 82, 20, 94 }) eFrame.dst[i++] = c;
        ProcessFrame(&eFrame);

        i = 0;
        for (unsigned char c : { 47, 22, 86, 45, 33, 38 }) eFrame.dst[i++] = c;
        ProcessFrame(&eFrame);

        // etc...

Reassignments like eFrame.Dst = { 47, 22, 86, 45, 33, 38 } would be neat ...but they are illegal :(

George Robinson
  • 1,500
  • 9
  • 21
  • 1
    Why aren't you using `std::array`? – Algirdas Preidžius Dec 28 '19 at 11:02
  • 1
    @ Algirdas: Because I cannot be certain how `std::array` would be arranged in memory. An `Ethernet Frame` must have a very specific bit layout. – George Robinson Dec 28 '19 at 11:04
  • "_Because I cannot be certain how `std::array` would be arranged in memory._" Why is that a problem? – Algirdas Preidžius Dec 28 '19 at 11:09
  • 1
    @ Algirdas: Because an `Ethernet Frame` must have a very specific bit layout for transmission over the network. See: https://en.wikipedia.org/wiki/EtherType – George Robinson Dec 28 '19 at 11:10
  • 1) One can obtain the pointer to the underlying buffer via `std::array::data`. So, unless you are doing something with the whole structure (not shown in your [mre]), the not knowing of memory layout, shouldn't be a problem. 2) The thing is: arrays in C++ are not assignable. You either use a wrapper, that allows said functionality, or do what you do. – Algirdas Preidžius Dec 28 '19 at 11:15
  • "*One can obtain the pointer to the underlying buffer via* `std::array::data`". Is this data guaranteed to be packed as by `#pragma pack(1)` ? – George Robinson Dec 28 '19 at 11:21
  • 1
    Yes, but I cannot conclude from it, that padding is not added. If it is, then it will destroy the layout of the `Ethernet Frame` – George Robinson Dec 28 '19 at 11:49
  • "_This container is an aggregate type with the same semantics as a struct holding a C-style array `T[N]` as its only non-static data member._" – Algirdas Preidžius Dec 28 '19 at 11:56
  • "*...as its only non-static data member*". What about its static members? – George Robinson Dec 28 '19 at 12:48
  • "_What about its static members?_" Why do those matter? `static` members aren't allocated in the same manner, or in the same block. – Algirdas Preidžius Dec 28 '19 at 13:39

2 Answers2

1

If you couldn't change struct ethernet_frame to use std::array, then there are still several one-liners you could use to copy data, albeit less safely. First, using good old memcpy():

#include <cstring>
...
memcpy(eFrame.dst, std::array<unsigned char, 6>{36, 84, 23, 77, 35, 11}.begin(), std::size(eFrame.dst));

Or using std::copy_n():

#include <algorithm>
...
std::copy_n(std::array<unsigned char, 6>{36, 84, 23, 77, 35, 11}.begin(), std::size(eFrame.dst), eFrame.dst);

With the Ranges TS, you can avoid having to specify the size of the data you want to copy:

#include <experimental/ranges/algorithm>
...
std::experimental::ranges::copy(std::array<unsigned char, 6>{36, 84, 23, 77, 35, 11}, eFrame.dst);

Also, if there is no one-liner for something you want to use more than once, then you can of course always write a function yourself that turns the problem into a one-liner.

G. Sliepen
  • 7,637
  • 1
  • 15
  • 31
0

Not really a one-liner, but a simple function that uses C++17 fold expressions:

template<typename... Ts, typename T>
void set_elements(T (&arr)[sizeof...(Ts)], Ts... values) {
    auto ptr = arr;
    ((*ptr++ = values), ...);
}

and then:

set_elements(eFrame.dst, 36, 84, 23, 77, 35, 11);
Evg
  • 25,259
  • 5
  • 41
  • 83