cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
90 stars 0 forks source link

Читаемый синтаксис для назначения назначения адресов. #204

Open apolukhin opened 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +3, -7 Автор идеи: avraliov.andrey

Изучая язык Ada захотелось такую же, человеческую возможность использовать читаемый и дубовый способ назначения адресного пространства идентификаторам. Без всяких reinterpret_cast. Очень не хватает в программировании embedded приложений.

В ada:

var: mod2*32;

for var'address use to_adress (16#405338#)

Хотелось бы в С++ что то в этом роде:

std::uint32_t var;

for var using address(405338);

Да, понимаю что нужно добавить новое ключевое слово в язык. Может кто предложит идею в более простой реализации. Только, думаю не стоит говорить что reinterpret_cast<>()... это вполне замечательный вариант. Да он работает, но читаемость кода заметно ухудшается со всеми вытекающими.

apolukhin commented 3 years ago

yndx-antoshkka, 12 июля 2017, 12:04 А такой вариант вас устроит:

template <uinptr_t A, class T>
struct dma: std::reference_wrapper<T> {
    dma() noexcept
        : std::reference_wrapper<T>{ *reinterpret_cast<T*>(A) }
    {}

    template <class U>
    explicit dma(U&& u) noexcept(is_nothrow_assignable_v<T, U>)
        : std::reference_wrapper<T>{ *reinterpret_cast<T*>(A) }
    {
        *this = std::forward<U>(u);
    }
};

// Usage:
dma<405338, std::uint32_t> var;
dma<40583, std::uint32_t> var = 100;

Бонусом можно будет навешать static_assert на alignment и платформо специфичные допустимые диапазоны адресов.

avraliov.andrey, 12 июля 2017, 15:25 yndx-antoshkka, Спасибо за ответ. Да это вариант, сам пользую подобные вещи. Это можно запихать в либу. Но! количество кода с читаемостью и последующим сопровождением на лицо. Страуструп на Cppcon2016 сам сказал что нужен компромисс между фичами в языки и библиотеках. Одни дают экспрессивность языка, другие время компиляции. Мне кажется что мой любимый С++ (как один из основных языков в этом программирования железа) ДОЛЖЕН делать это просто и дубово, с проверкой типа, что это не просто int, а адрес на уровне языка. Развитие темы мне кажется это так же как ada - простой синтаксис для битовых полей указанной переменной (насколько я зная битоые поля в c++ оставлены на откуп разработчика компилятора.). в ada: for var at 0 range 0 .. 3; что означает в переменной var в нулевом байте использовать только первые четыре бита. Все с гарантией от стандарта на уровне языка.

Как это будет выглядеть на С++ мы с вами представляем!!! Разница есть

yndx-antoshkka, 12 июля 2017, 15:43 avraliov.andrey, предлагаю продумать следующую идею:

"Набор функций и классов, полезных для embedded разработки."

Давайте соберём побольше полезных классов/конструкций языка и напишем proposal сразу на добавление их всех. Если есть уже готовая популярная библиотека - дайте на неё ссылку, задача упростится :)

avraliov.andrey, 12 июля 2017, 16:43 yndx-antoshkka, Библиотек таких не видел. Много embedded кода вежде на обычном С. Энтузиасты С++ выкручиваются сами способами типа вашего. Лично я пока не вижу способа решить проблему без дополнения именно синтаксиса языка, но я программист любитель (по профессии я врач), до уровня Антона Полухина мне не угнаться. Но если вы с высоты своего опыта видите что это реально сделать имеющимися средствами хотя бы С++17, то скажите. Я буду думать, искать и пробовать. И на какой площадке обсуждать, куда присылать?

yndx-antoshkka, 12 июля 2017, 17:16 avraliov.andrey, обсуждать стоит здесь, присылать сюда же :)

У меня нет особого опыта в embedded разработке, я не знаю типичных задач и проблем embedded разработчика. Так что нужен человек, который будет активно жаловаться на что, чего ему не хватает в C++

avraliov.andrey, 12 июля 2017, 18:08 yndx-antoshkka, Ну мне после приобретенного опыта в ada программировании стало очевидно, что в С++ нужен способ удобного и гарантированного назначения адреса переменным, удобная и гарантированная работа с битовыми полями. что бы можно было написать:

typename enable_t;
for enable using size 1; //(bit)

typename reset_t;
for reset using size 1; //(bits)

typename data_t;
for data using size 8; //(bits)

struct status_register
{
    enable_t en;
    reset_t res;
    data_t data;
};
for status_register using size 16;
for status_register using address 34657678;
for status_register.en using range 0..1;
for status_register.reset using range 1..2;
for status_register.data using range 8..15;

На выходе получаем регистр с адресом и использьванием только нужных битов: 1100000011111111 по адресу ххх.

Читаемость и программируемость как мне кажется на высоте.

Если битовые операции делать через |= &= ~=, то мне кажется это уже не с++ а галимый С, от которого мы вроде пытаемся уйти в сторону типобезопастности , читаемости, выразительности...

yndx-antoshkka, 13 июля 2017, 17:08 avraliov.andrey, Да, c полями битов в стандарте так себе:

[class.bit] Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined.

Надо подумать, как это исправить, ничего не разломав. По идее нужен специальный синтаксис, который говорит что биты идут строго в том порядке, что написали

avraliov.andrey, 17 июля 2017, 9:46 yndx-antoshkka, абсолютно с вами согласен!!!

avraliov.andrey, 23 июля 2017, 20:15 yndx-antoshkka, нашел в инете интересное решение. Не то что хотелось бы в идеале но тем не менее.

#include <type_traits>
#include <cstddef>
#include <cstdint>

template <std::size_t LastBit>
struct MinimumTypeHelper {
typedef
    typename std::conditional<LastBit == 0 , void,
        typename std::conditional<LastBit <= 8 , std::uint8_t,
            typename std::conditional<LastBit <= 16, std::uint16_t,
                typename std::conditional<LastBit <= 32, std::uint32_t,
                    typename std::conditional<LastBit <= 64, std::uint64_t,
    void>::type>::type>::type>::type>::type type;
};

template <size_t Index, size_t Bits = 1>
class BitField {
private:
    enum {
        Mask = (1u << Bits) - 1u
    };

    typedef typename MinimumTypeHelper<Index + Bits>::type T;
public:
    template <class T2>
    BitField &operator=(T2 value) {
        value_ = (value_ & ~(Mask << Index)) | ((value & Mask) << Index);
        return *this;
    }

    operator T() const { return (value_ >> Index) & Mask; }
    explicit operator bool() const { return value_ & (Mask << Index); }
    BitField &operator++() { return *this = *this + 1; }
    T operator++(int) { T r = *this; ++*this; return r; }
    BitField &operator--() { return *this = *this - 1; }
    T operator--(int) { T r = *this; --*this; return r; }

private:
    T value_;
};

template <size_t Index>
class BitField<Index, 1> {
private:
    enum {
        Bits = 1,
        Mask = 0x01
    };

    typedef typename MinimumTypeHelper<Index + Bits>::type T;
public:
    BitField &operator=(bool value) {
        value_ = (value_ & ~(Mask << Index)) | (value << Index);
        return *this;
    }

    explicit operator bool() const { return value_ & (Mask << Index); }

private:
    T value_;
};

union reg
{
    std::uint32_t raw;
    BitField<0,1> en;
    BitField<1,1> rst;
    BitField<5,3> data;
};

int main()
{
    reg my_reg;

    my_reg.data = 4;

    return 0;
}