Перейти к содержанию
    

Плавный переход C -> C++ под МК

В 01.03.2024 в 23:52, Arlleex сказал:

Нет, неудобно при сериализации данных. Нужны именно псевдомассивы.

А зачем "псевдо"? Ведь данные всё равно лежат в реальном массиве. Его и используйте. Если есть несколько таких структур, различающихся только размерами массивов, можно приводить к максимальному.

Ну или взять std::span из c++20. Хотя это не отменяет надобности в массиве для собственно хранения данных.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, AHTOXA сказал:

А зачем "псевдо"? Ведь данные всё равно лежат в реальном массиве. Его и используйте. Если есть несколько таких структур, различающихся только размерами массивов, можно приводить к максимальному.

Дык не знаю я, какого он размера, этот самый массив.

Думаю исходная задача Вам понятна, но на всякий случай вкратце повторюсь.

По некой линии связи гуляет протокол, пакетный/байтовый - не важно. Я отправляю фреймы данных, и мне отправляют тоже. Есть фреймы, в которых сразу все понятно - что делать (всякие командные фреймы), сколько писать или читать (фреймы с какими-то данными известной длины), а есть фреймы, в которых ситуативно, время от времени, область данных может иметь разную длину. В качестве примера: я с GUI-шной программулины на ПК отправляю на железку UDP-кадр со структуркой, в которой предусмотрено поле "команда" для парсинга. Так вот, отправляю я команду "читать приемный FIFO CAN-интерфейса". Железка смотрит на заполненность FIFO и в зависимости от этого (ну, от возможности запихнуть разом в один UDP-кадр ответа тоже) выгребает сколько-то CAN-фреймов из этого FIFO и формирует ответную UDP-каракатицу. Если в CAN FIFO было одно сообщение, то отправится UDP-ответ вида [CAN_FIFO_READ_ACK]-[FRAME 1], если 3 то [CAN_FIFO_READ_ACK]-[FRAME 1]-[FRAME 2]-[FRAME 3] и т.д. А если FIFO вовсе пуст, то отправится ответ только с заголовком, в котором помимо CAN_FIFO_READ_ACK будет установлен флажок пустого FIFO, хотя это можно и по длине полезных данных UDP-фрейма оценить - вольность реализации, короче.

Перекладывая это на язык программирования, в Си как раз для этого делался структурный "хак" в виде FAM - гибкого последнего члена

struct sCANFrame;

typedef struct {
  ... // всякая служебная фигня, в том числе куда кладется признак-ответ CAN_FIFO_READ_ACK
  sCANFrame frame[];
} sUDPAckReadFIFOCAN;

и этот безразмерный frame[] для компилятора и был "псевдо", потому что реальной памяти он не создавал, в операциях sizeof() не участвовал, красота. Использовать такую структуру как раз было удобным при парсинге сообщений неизвестной длины поля данных. Анализируется заголовок, вычисляется любым удобным способом количество мемберов sCANFrame за этим заголовком, а потом из этой же структуры sUDPAckReadFIFOCAN получаем доступ к нужным мемберам через этот фиктивный массив. "Круто! Да это ж круто!!!" (С)

И вот настало время формализовать грамотный перевод этой возможности на язык C++, и я столкнулся с тем, что каждый первый лишь пользуется расширениями своего компилятора (и я в том числе) - потому что стандартом плюсов такой финт ушами уже запрещен синтаксически. А ушами то финтить еще хочется - вот и вопрос, что дали девелоперы плюсов взамен. Или кнутом дали, а пряник забыли?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 3/3/2024 at 11:32 AM, Arlleex said:

потому что стандартом плюсов такой финт ушами уже запрещен синтаксически. 

Был бы запрещен - была бы ошибка.
А раз предупреждение, то всего лишь не рекомендован :blush:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 минут назад, dimka76 сказал:

Был бы запрещен - была бы ошибка.
А раз предупреждение, то всего лишь не рекомендован :blush:

ISO/IEC 14882:2003 8.3.4/1:

Цитата

If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero.

Обычно в стандартах выражение "shall be" относится в разряд тех, которым в дисклеймере документа уделяется особое значение.

Думаю, в "чистых" плюсах, все-же, будет какая-то ошибка или предупреждение🙂

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, Arlleex сказал:

и этот безразмерный frame[] для компилятора и был "псевдо", потому что реальной памяти он не создавал, в операциях sizeof() не участвовал, красота. Использовать такую структуру как раз было удобным при парсинге сообщений неизвестной длины поля данных. Анализируется заголовок, вычисляется любым удобным способом количество мемберов sCANFrame за этим заголовком, а потом из этой же структуры sUDPAckReadFIFOCAN получаем доступ к нужным мемберам через этот фиктивный массив. "Круто! Да это ж круто!!!" (С)

Это всё понятно. Но всё же, реальная-то память под эти элементы массива где-то выделена? То есть, по сути, мы "наводим" наш "безразмерный массив" на лежащий где-то реальный массив.

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

то есть или так:

struct sUDPAckReadFIFOCAN1{
  Header header;
  size_t frameSize;
  sCANFrame frame[100500];
};

или так:

struct sUDPAckReadFIFOCAN1{
  Header header;
  std::span<sCANFrame> frames;
};

ЗЫ. Во втором случае, конечно не удастся просто привести указатель на буфер приёма/передачи к этой структуре. Это может быть как минусом, так и плюсом, в зависимости от обстоятельств.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, AHTOXA said:

либо использовать современный класс span

ага, начиная с C++20 ))

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

59 минут назад, AHTOXA сказал:

Это всё понятно. Но всё же, реальная-то память под эти элементы массива где-то выделена? То есть, по сути, мы "наводим" наш "безразмерный массив" на лежащий где-то реальный массив.

Ну по факту да, выделена. Но не на 100500 элементов, разумеется, а на максимальный размер оставшегося места в UDP-кадре))

Но в таком случае я мог бы и 1 элемент всего лишь указать - с выделенной памятью компилятору ведь все равно, что я индексом пошел дальше))

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

Просто я активно пользовался sizeof() применительно к структуре с гибким членом, потому что он вернет размер всего, кроме этого массива. Да и как-то нагляднее было, что пустой массив в описании структуры сразу давал однозначное понимание, как с ней работать и что при работе с ней предполагается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Второй раз возвращаюсь к вопросу, который ранее уже писал тут. Просто ясности до сих пор у меня нет.

Итак, за всеми этими модными и витиевато описанными в стандарте Name Lookup на десятках страниц, а также иногда затрагиваемыми гуру из яндекс-практикумов и лекций на ютубе от них же, я вынес, что в C++ есть (должен быть?) некий механизм дедукции, чтобы не писать многосимвольные портянки пробираний внутрь самого глубоко закопанного namespace или класса, например.

Так вот. Могу ли я определить

namespace {
  enum eCmd {
    GPIO_CTRL
  };
  
  struct sGPIOCmdDesc {
    enum ePin {
      SET_EN_DISPLAY
    };
    
    eCmd cmd : 3;
    ePin pin : 1;
  };
}


и объявить с человеко-читаемой инициализацией (у меня не получилось, компилятор ругается, разумеется, на незнание GPIO_CTRL и SET_EN_DISPLAY)

sGPIOCmdDesc descriptor = {GPIO_CTRL, SET_EN_DISPLAY};

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

Вместо

sGPIOCmdDesc descriptor = {sGPIOCmdDesc::GPIO_CTRL, sGPIOCmdDesc::SET_EN_DISPLAY};


Представьте, что вложенность namespace или, там, структур-классов хотя бы больше 3 - это уже кошмарно читается.

Насчет сокращения имен по типу using A = sGPIOCmdDesc::eCmd я знаю, однако это все равно костыль. Не красиво это читается, хочется верить что годы работы над стандартом C++ были потрачены для удобства написания и чтения кода, а не ради размусоливания страниц стандарта и гарантии, что ни один человек на земле не будет нормальные читаемые программы на нем писать.

Про using namespace ... я тоже знаю, но это объявление не имеет "отмены", по аналогии с #undef у #define.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 часов назад, Arlleex сказал:

Про using namespace ... я тоже знаю, но это объявление не имеет "отмены", по аналогии с #undef у #define.

Если поместить конструкцию using namespace в функцию, то область её действия заканчивается в конце функции:
 

namespace Test
{
struct Foo { 
  int flag
};
}

auto makeFoo()
{
    using namespace Test;
    return Foo {1};
}

int main()
{
    auto foo = makeFoo();
    // здесь не действует using namespace Test
}

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

sGPIOCmdDesc descriptor = {sGPIOCmdDesc::GPIO_CTRL, sGPIOCmdDesc::SET_EN_DISPLAY};

не выглядит ужасной, зато сразу видно, что мы не перепутали enum-ы. А если вместо enum использовать enum class, то мы также можем быть уверены, что мы не перепутали местами аргументы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 минут назад, AHTOXA сказал:

Если поместить конструкцию using namespace в функцию, то область её действия заканчивается в конце функции...

О, мысль с auto мне пригодится. Спасибо.

Цитата

Запись вида ... не выглядит ужасной

Выглядит, выглядит)) Ведь здорово было бы, если в списке инициализации {} неявно делался бы using (namespace/class/что там еще бывает). И если указанного enum в этом namespace не было найдено, компилятор выполняет классический поиск имен - поэтапно "выходя" из всех вложенных пространств до самого глобального. А когда не нашел, или увидел неоднозначность при нескольких кандидатах (ambiguous), пусть так и написал бы - т.е. логика простейшая была бы, в целом, она щас такая и есть🙂

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 часов назад, Arlleex сказал:

чтобы не писать многосимвольные портянки пробираний внутрь самого глубоко закопанного namespace

A если using mamespace Xxx:Yyy:Zzz, ну и вообще обычный using Vava = Zzzz::A<B, C, Ё> и тому подобные конструкции?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 минут назад, EdgeAligned сказал:

ну и вообще обычный using Vava = Zzzz::A<B, C, Ё>

А это пример щас из головы был или это реально есть возможность селективно "собирать" нужную сборную солянку из разных enum-ов, например?🙂

Вообще, было бы круто иметь такое.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну какбы да, сократить запись всех имен можно:

namespace Name {
template<typename T>
class A {
public:
	enum En {
		EN_0, EN_1
	};

	En val;
};
}
....
  	using e = Name::A<int>::En;
	obj.val = e::EN_0;

Однако, совсем выкинуть имя энумератора не получится, ибо он разделяет области видимости имен констант в энумераторе. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 минут назад, EdgeAligned сказал:

Однако, совсем выкинуть имя энумератора не получится, ибо он разделяет области видимости имен констант в энумераторе. 

А что дает <int> при таком объявлении using e?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И еще. Про различия между enum и emum class.

Когда пишете просто enum, то члены энумератора могут быть без проблем преобразованы в числовые значения:

enum En {
	EN_0, EN_1
};
...
int val = En::EN_1;

а вот если напишите 

enum class En {
	EN_0, EN_1
};

то то же приведение к int уже не сработает. В этом случае энумератор строго типизирован и безопасен по типу, и приведение возможно будет только так:

int val = static_cast<int>(En::EN_1);

 

Только что, Arlleex сказал:

А что дает <int> при таком объявлении using e?

Не, это я просто показал длину строки, состоящей из всяких там наворотов, кучи классов-имен-шаблонов. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...