Jump to content

    
Arlleex

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

Recommended Posts

43 минуты назад, one_eight_seven сказал:

Непонятна задача полностью, вы показываете фрагмент...

Я сейчас занимаюсь разработкой небольшого протокола поверх CAN для своих устройств. Основная идея заключается в том, что девайсы разные и имеют разную логическую репрезентацию для пользователя. Например, какой-нибудь датчик, внутри которого можно выделить следующие логические блоки: модуль семплирования, модуль самодиагностики, модуль настроек и управления поведением датчика. Каждый логический блок (который с точки зрения протокола я называю Node) имеет свой набор CAN-ID, в который входят рабочие ID (по которым, собственно, девайс выдает и принимает информацию в штатном режиме в соответствии со своей задачей), и сервисные, которые позволяют узнать имя того или иного узла или ассоциированного ID. Это нужно для того, чтобы (в будущем) на ПК-шной программе я мог запустить кнопкой "Поиск устройств на шине" процедуру обнаружения девайсов с заранее неизвестным наличием тех или иных. И в результате должен увидеть графическое дерево, сначала глобальное, на котором узлами являются сами устройства, а кликая на девайс - я должен видеть его логическую структуру с маркированными адекватными понятными именами модулями, составом рабочих ID, кто за что отвечает. Это основная идея. CANOpen не предлагайте:smile:

Но для того, чтобы изолировать логику самого протокола от юзер-зависимых данных (на текущий момент это таблица всех ID, прошиваемых пользователем на этапе конфигурации конкретного экземпляра девайса), приходится делать небольшие "прослойки" в виде дополнительных таблиц, которые, помимо содержания тех самых "имен" CAN-ID, должны иметь одинаковый интерфейс для кода самого протокола при смене порядка следования/добавлении новых ID в настроечной таблице (CANIDTbl). Поэтому протокол опирается именно на эту "промежуточную" таблицу, чтобы в 10 разных местах не править логику ассоциации CAN-ID-интерфейсов, а только при необходимости в одном. Пока что как-то так. Я пока в процессе больше художественном, так что за день может поменяться многое.

Поэтому я еще не приступая к непосредственной логике протокола (обработка запросов, выдача сервисных данных и т.д.) должен ввести некоторые константные структуры и таблицы с синтаксической возможностью задавать некоторые связи "парами", т.е. заранее определенными числами. Тут нет никакого ООП - банальное размещение объектов и ручная инициализация.

Share this post


Link to post
Share on other sites
30 minutes ago, Arlleex said:

CANOpen не предлагайте

Почему надо обязательно изобрести лисапед придумать заново уже готовое существующее решение, причем, исходники и дока валяются на каждом углу?

Тут наверно что-то военное/секретное )))

Share this post


Link to post
Share on other sites
1 час назад, Arlleex сказал:

Т.е. смотрим ITEM(): индексы идут (и могут идти) не по порядку. Чуть позже я хотел создать #define-ны с осмысленными названиями индексов. Но, раз в C++ (по стандарту, что ли, получается?) нельзя выборочно инициализировать элементы массива (а также в любом синтаксически указанном порядке),

Почему это нельзя? Вот:

  enum { 
    TEMP_CHA_NTC1, TEMP_CHA_NTC2, TEMP_CHA_NTC3, 
    TEMP_CHA_PT1, TEMP_CHA_PT2,                  
    TEMP_CHA_IRED,                               
    TEMP_CHA_n
  };
  static u8 const defTyp[TEMP_CHA_n] = {
    [TEMP_CHA_NTC2] = TYP_BOSCH_NTC,
    [TEMP_CHA_NTC1] = TYP_BOSCH_NTC,
    [TEMP_CHA_NTC3] = TYP_BOSCH_NTC,
//    [TEMP_CHA_PT1] = TYP_PT1000,
    [TEMP_CHA_PT2] = TYP_PT1000,
    [TEMP_CHA_IRED] = TYP_YASA_P400};

IAR проглатывает это молча. c++

 

PS: В Вашем коде "нельзя" потому, что есть конструктор sCANIDTbl(). Уберите его и будет льзя.  :wink:

А с конструктором инициализации типа: type array[] = {...};  запрещены. Любые. Даже не выборочные. Тут уж "трусы надень или крестик сними".  :unknw:

Share this post


Link to post
Share on other sites
54 минуты назад, Forger сказал:

Почему надо обязательно изобрести лисапед придумать заново уже готовое существующее решение...

Г*вно потому что этот CANOpen. Не нравится мне. Аргументировать не буду - протокол "для своих":smile:
 

47 минут назад, jcxz сказал:

Почему это нельзя?

Вот и я не знаю, почему комитет так решил.

Цитата

IAR проглатывает это молча...

Keil тоже проглатывает, но выдает Warning, мол такая инициализация только в C99 разрешена. Нет ее в C++. Но это я про структуры. В Вашем примере массив u8 - а это не структура и там правила инициализации несколько другие, так что может и работать...

Цитата

В Вашем коде "нельзя" потому, что есть конструктор sCANIDTbl()...

Из-за умников в комитете, а не из-за конструктора. В моем случае пользовательский конструктор по умолчанию нужно объявить, иначе - ошибка компиляции. Вчера выяснили. __no_init, скорее всего, снимает требование объявить конструктор у IAR, поэтому там работает.

Цитата

А с конструктором инициализации типа: type array[] = {...};  запрещены. Любые...

Так у меня в той структуре нет конструкторов.

P.S. Это я на ARM Compiler 6.14 сейчас. Попробовал ради интереса на 6.12 скомпилировать - съел без предупреждений:to_take_umbrage:

Share this post


Link to post
Share on other sites
11 минут назад, Arlleex сказал:

Так у меня в той структуре нет конструкторов.

Скормил ваш код IAR-у: скомпился молча и без варнингов (код вставил "как есть", только добавил #define MAX_CANID_QNT 3).

И ещё откорректировал на: __root volatile const sCANIFDesc CANIFDesc[MAX_CANID_QNT] = ...

чтоб не выкидывал за неиспользованием.

Share this post


Link to post
Share on other sites
6 минут назад, jcxz сказал:

Скормил ваш код IAR-у: скомпился молча и без варнингов (код вставил "как есть", только добавил #define MAX_CANID_QNT 3).

Вверху дописал только что.

Видимо, от версии к версии различные взгляды на "правильность" Стандарта, и чем свежее компилятор, тем строже соблюдение правил...

Share this post


Link to post
Share on other sites

Я тут снова всех приветствую! Дисклеймер: :dash3:

Кусочек для примера

#define MAX_CANIF_QNT 3

typedef struct
{
  u32 id   : 29;
  u32 ext  :  1;
#define CAN_IDDIR_RX    0
#define CAN_IDDIR_TX    1
  u32 dir  :  1;
#define CAN_IDTYPE_SVC  0
#define CAN_IDTYPE_WORK 1
  u32 type :  1;
}sCANID;

typedef struct
{
           const char   *const name;
           const void   *const hnd;
  volatile const sCANID *const id;
}sCANIF;

...

static const sCANID CANIDTbl[MAX_CANIF_QNT] =
{
  {0, 0, CAN_IDDIR_RX, CAN_IDTYPE_SVC},
#ifdef PROJSET_CAN_RXID_STD
  {PROJSET_CAN_RXID, 0, CAN_IDDIR_RX, CAN_IDTYPE_SVC},
#else
  {PROJSET_CAN_RXID, 1, CAN_IDDIR_RX, CAN_IDTYPE_SVC},
#endif
#ifdef PROJSET_CAN_TXID_STD
  {PROJSET_CAN_TXID, 0, CAN_IDDIR_TX, CAN_IDTYPE_SVC}
#else
  {PROJSET_CAN_TXID, 1, CAN_IDDIR_TX, CAN_IDTYPE_SVC}
#endif
};

const sCANIF CANIFTbl[MAX_CANIF_QNT] =
{
#define ITEM(p, n, h) {n, (const void *const)h, &CANIDTbl[p]}
  ITEM(0, "XCAN RESERVED ID",     NULL),
  ITEM(1, "ROOT:UHMIM:SVC RX IF", &RootNode),
  ITEM(2, "ROOT:UHMIM:SVC TX IF", NULL)
#undef ITEM
};

...

u32 can_SendMsg(sCANID id, u8 msg[], u8 len)
{
  ...
}


// использую
void my_func(void)
{
  can_SendMsg(*CANIFTbl[1].id, ...);
}


Выдает

Цитата

error: no matching constructor for initialization of 'sCANID'
note: candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile sCANID') would lose volatile qualifier
note: candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile sCANID') would lose const and volatile qualifiers
note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided


Я так полагаю, что нужно предоставить конструктор копирования, только вот каким образом?

P.S. Решение (нашел для себя, читая про конструкторы копирования)

typedef struct sCANID
{
  u32 id   : 29;
  u32 ext  :  1;
#define CAN_IDDIR_RX    0
#define CAN_IDDIR_TX    1
  u32 dir  :  1;
#define CAN_IDTYPE_SVC  0
#define CAN_IDTYPE_WORK 1
  u32 type :  1;
  
  sCANID() {}
  sCANID(u32 id, u32 ext, u32 dir, u32 type) :
    id(id), ext(ext), dir(dir), type(type) {}
  sCANID(volatile const sCANID &i) :
    id(i.id), ext(i.ext), dir(i.dir), type(i.type) {}
}sCANID;

Share this post


Link to post
Share on other sites

Комитет не избавлялся ни от чего. Напомню что ++ ответивились от C98. И потом комитет лишь добавлял то что появлялось в последующих сях, а иногда не добавлял. Вопрос насчет разницы между конструкциями С и С++ очень интересный и в нём много интересных мозгокрутящих моментов. Но с практической точкии зрения мало нужный, считайте что ++ почти совместимы (за исключением именованных инициализаций, массивов переменной длинны в аргментах и тд). По поводу именовванных инициалзаций, понять комитет вполне возможно. Вместо них используйте конструкторы. Еще советую навсегда забыть про битовые поля (особенно в комбинации с volatile), если нет желания прыгать на граблях.

Share this post


Link to post
Share on other sites
2 часа назад, Kabdim сказал:

Еще советую навсегда забыть про битовые поля

От я дурак! Битовую арифметику компилятору доверяю. Оказывается солнце-то вручную закатывать надо! Пойду перелопачивать свои исходники... :cray:

Share this post


Link to post
Share on other sites

 

5 hours ago, Kabdim said:

Еще советую навсегда забыть про битовые поля (особенно в комбинации с volatile), если нет желания прыгать на граблях.

Давно успешно пользую union/struct в комбинированном составе и при этом все работает чотко и одинаково на любом компиляторе и в любом проце.

Может дело и не в битовых полях, а в неудачно разложенных в исходниках граблях? ;)

Share this post


Link to post
Share on other sites
On 6/11/2021 at 3:05 PM, jcxz said:

Почему это нельзя?

Потому что инициализация выборочных полей массива описана в стандарте C99 (вот тут), но не описана ни в каком стандарте C++. Каких-то черновиков для включения в стандарт вроде б нет (но я очень невнимательно слежу за деятельностью комитета по стандартизации).

В прошлом году (наконец-то!) подобный механизм для структур перетащили из C99 в C++20 (см. пункты 3, 4). Так что iar молодцы, конечно, но при перетаскивании этого кода куда-то ещё будут приключения.

 

8 hours ago, Arlleex said:

volatile const sCANID *const id;

Я невнимательно читал тему. Это у вас настройки такие, что-ли? Иначе зачем так делать?

 

8 hours ago, Arlleex said:

typedef struct { ... }sCANID;

Микро-совет. Если пишете на плюсах, пишите просто struct Name { ... };
То же самое по смыслу, но а) букв меньше б) в IDE навигации по коду не вылезает анонимная структура.

Share this post


Link to post
Share on other sites
46 минут назад, esaulenka сказал:

Я невнимательно читал тему. Это у вас настройки такие, что-ли? Иначе зачем так делать?

Да, хранятся отдельной таблицей в отведенном секторе Flash-памяти.
В дальнейшем указатель ссылается либо на настройки из Flash (когда заданы), либо на дефолтные.

Цитата

Микро-совет...

Это я знаю, просто в .h-никах из-за такой записи редактор подсвечивает синтаксис красным, показывая, мол, что перед Name должно быть ключевое слово 'struct'. Когда .h переименовываешь в .hpp, то косяк исчезает. Конечно, оно компилируется и так, и так. Сейчас у меня .h переименовался в .hpp, так что там это исправлено. Для себя сделал небольшое отличие .h- от .hpp-файлов: если никаких плюсовых фишек не используется, то это .h, чтобы можно было его подключить к пуре .c-исходнику. Как только появляются какие-то плюсовые штучки, то файл становится .hpp:smile:

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.