Список инициализации полей const и структуры массива - PullRequest
3 голосов
/ 18 октября 2019

Совсем недавно начал программировать на C ++ для микроконтроллеров, и я столкнулся с ситуациями *, когда было бы удобно иметь нестатическое поле const в структуре, которая всегда гарантированно имеет фиксированное значение (одинаково для всехэкземпляр sturct, когда-либо).

Учитывая структуру

struct S {
    const uint8_t c; // Should always be 42
    char v;
    uint32_t arr[4];
}

Я бы хотел, чтобы c было постоянным значением и всегда одинаковым постоянным значением. Мне бы хотелось использовать удобство списков инициализаторов скобок для установки v и членов arr, таких как

S some_var = {'v', { 0, 1, 2, 3 } };

, поскольку я хотел бы, чтобы c былконстанта, у меня сложилось впечатление, что я должен использовать список инициализатора для установки c, такой как S() : c(42) {}, который работает просто отлично, если я не пытаюсь также инициализировать arr, в которомслучай, когда я потерялся на том, как должен выглядеть список. Это выполнимо с помощью C ++ 11? (Также интересует ответ, если это невозможно в C ++ 11, но в каком-то более новом стандарте.)

Пример кода:

#include <stdio.h>
#include <stdint.h>

struct S {
    const uint8_t c; // Should always be 42 on every instance
                     // of the struct due to hardware shenanigance
                     // (i.e. this struct is the representation of a register value)
    char v;
    uint32_t arr[4];

    // This allows using "S s1;"
    S() : c(42), v('a'), arr{} {}

    // This allows using "S s2 = { 'v', 0, 1, 2, 3 };" works but it's clumsy:
    S(uint32_t v, uint32_t arr0, uint32_t arr1, uint32_t arr2, uint32_t arr3) :
        c(42), v(v), arr{ arr0, arr1, arr2, arr3 } {}

    // I would like to do something along the lines of "S s2 = { 'v', { 0, 1, 2, 3 } };":
    // S(uint32_t v, uint32_t arr[4] /*?*/) :
    //     c(42), v(v), arr{/*?*/} {}

};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    // Initialiation like this works with the line:12 signature:
    S s2 = { 'v', 0, 1, 2, 3 };

    // I'd like to initialize like this:
    // S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

* Контекст о том, почему яхотел бы сделать это: это может показаться странным, потому что, если значение всегда одинаково, зачем его хранить? Хорошо представьте, что рассматриваемая структура является битовым полем, которое соответствует регистру ИС, с которой связывается микроконтроллер. Эти регистры иногда имеют «зарезервированные» поля, и таблица данных указывает, какое значение вы должны записать в эти поля. С точки зрения программиста, было бы удобно, если бы мне никогда не приходилось иметь дело с установкой указанных битов вручную.

1 Ответ

6 голосов
/ 18 октября 2019

C ++ 11 дает вам std::array, который похож на необработанный массив, но не содержит никаких «негативов» (распад массива, копировать нельзя). Используя это, вы можете получить именно то, что вы хотите, как

struct S {
    const uint8_t c = 42;
    char v = 'a';
    std::array<uint32_t, 4> arr{};

    // This allows using "S s1;"
    S() {}

    S(uint32_t v, std::array<uint32_t, 4> arr) : v(v), arr{arr} {}
};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

, который выводит

s1.c = 42
s1.v = 'a'
s1.arr[3] = 0
s2.c = 42
s2.v = 'v'
s2.arr[3] = 3

Если вам необходимо иметь необработанный массив в S, тогда ваш другой вариантэто использовать std::initializer_list в конструкторе. Это будет выглядеть как

struct S {
    const uint8_t c = 42;
    char v = 'a';
    uint32_t arr[4]{};

    // This allows using "S s1;"
    S() {}

    S(uint32_t v, std::initializer_list<uint32_t> data) : v(v)
    {
        int i = 0;
        for (auto e : data)
            arr[i++] = e;
    }
};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

И вы получите те же результаты, что и код, используя std::array.

...