Jump to content

    
Arlleex

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

Recommended Posts

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

Вот в таких случаях и появляется необходимость в кардинальной переделке.

Я не исключаю каких-то отдельных случаев, когда переделка действительно необходима. Но примеры, которые Вы привели, как раз подразумевают переделку железа в том числе - а это вполне себе аргумент при планировании работ по доработке или выпуску новой версии девайса. На это нужно выделять и время, и деньги, соответственно. Иначе хотелки начальства или фирмы в целом таким образом можно крутить на карусели до бесконечности, особенно если и планирования, как такового, никогда не было (а-ля есть идея - давайте продавать). По поводу SPI. Приведенный Вами пример напомнил мне I2C. Я как-то написал арбитр доступа к шине, который позволяет обращаться к различным устройствам. Причем для добавления поддержки конкретной микросхемы пишется лишь протокол обмена с этой микросхемой (какой адрес, сколько байт слать и т.д.). Все остальное берет на себя арбитр. Удобно, очень даже. Причем реализация таскается из проекта в проект. В планах была еще идея приоритетных транзакций а также прерываемых транзакций (это для случаев, когда, например, хочется одновременно скачивать over-дохрена-байт с FRAM или Flash с девайса на ПК и при этом еще работать с другими абонентами на шине). Но все это вопрос лишь времени - если оно есть, можно посидеть пописать такой универсальный арбитр на почти все случаи жизни. С другой стороны - если требуется писать "быстро" и "чтоб завтра уже результат увидеть хоть какой-то", то, наверное, да - пишем как можно быстрее и при этом не в минус функционалу.

Share this post


Link to post
Share on other sites
46 minutes ago, Xenia said:

Переименовываешь расширение имени файла с *.c на *.cpp - вот и весь переход :).

Всего лишь третья страница холивара, а уже пошли в ход козыри :dirol:

Share this post


Link to post
Share on other sites
13 minutes ago, Forger said:

Всего лишь третья страница холивара, а уже пошли в ход козыри :dirol:

При таком количестве гуру программирования - это только начало. А сколько их еще на просторах Киргизии и Узбекистана !

Share this post


Link to post
Share on other sites

...

1 час назад, x893 сказал:

Конечно. Поменяете R0 в своем методе и капец этой чудо программе.


Дабы место не пропадало зря, спрошу вот какую штуку. Допустим, есть у меня кольцевой буфер, реализованный без использования критических секций. То есть есть один владелец на чтение (тот процесс, который только читает данные из очереди) и один на запись. Каждый модифицирует свой указатель, чужой только читает (при необходимости). Собственно, вот кусочек из реализации

s32 cRingBuf::Read(RBTYPE buf[], s32 len)
{
  s32 rlen = GetBusy();
  if(rlen > len) rlen = len;
  len = rlen; s32 rp = this->rp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; rp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; rp &= (rp - RB_BUF_SIZE) >> 31)
#endif
    *buf++ = this->buf[rp++];
  this->rp = rp; return rlen;
}

s32 cRingBuf::Write(RBTYPE buf[], s32 len)
{
  s32 wlen = GetFree();
  if(wlen > len) wlen = len;
  len = wlen; s32 wp = this->wp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; wp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; wp &= (wp - RB_BUF_SIZE) >> 31)
#endif
    this->buf[wp++] = *buf++;
  this->wp = wp; return wlen;
}


Теперь вот что: должен быть метод для освобождения некоторого количества элементов из очереди. Получается, что для читателя и писателя - это тоже разные методы, т.к. на этом базируется соблюдение целостности доступа при отсутствии критических секций в любом их проявлении. Назовем их FreeR(), FreeW(). И даже методов для полной очистки очереди, по-хорошему, должно быть 2: rp = wp для читателя, wp = rp для писателя. Теперь, собственно вопрос: а можно ли как-то контекстно выделить функцию или кусок исходника, в котором вызов Free() автоматически компилятором заменится на вызов конкретной FreeR() или FreeW()? Или старый добрый #define Free(obj) obj->FreeR() и в конце участка #undef Free есть решение этого вопроса?

Share this post


Link to post
Share on other sites
1 hour ago, Arlleex said:

Теперь, собственно вопрос: а можно ли как-то контекстно выделить функцию или кусок исходника, в котором вызов Free() автоматически компилятором заменится на вызов конкретной FreeR() или FreeW()?

Это называется полиморфизм. Виртуальная функция Free, которая переопределяется в двух наследниках буфера - один наследник читатель, второй - писатель.

Но помимо запутанного кода (три класса вместо одного) будет оверхед виртуальных функций. Кажется, можно этого избежать (гуглить CRTP) но я б такое не делал - серьезное усложнение исходников на ровном месте. 

Share this post


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

Виртуальная функция Free, которая переопределяется в двух наследниках буфера - один наследник читатель, второй - писатель.

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

Share this post


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

Но раз он будет абстрактным, то какого типа создавать сам объект очереди?

Да, вы правы, фигня получается. Должен заработать только статический полиморфизм (см. мою ссылку выше) с явным приведением типа очереди в читателе и в писателе. Но за такое явное приведение в приличном обществе будут бить...

Share this post


Link to post
Share on other sites
12.05.2021 в 12:32, Arlleex сказал:

нужно уйти от структурного программирования к ООП.

ООП уже прошлый день. ДОП грядет

12.05.2021 в 10:33, Arlleex сказал:

Давно заметил, что компилятор C++ порождает очень даже оптимальный код. Много интересных механизмов заложено в самом принципе ООП, которые хотелось бы изучить и применять в разработке ПО

Перелез с С++ на С - чую щастье гдето рядом. 

Share this post


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

Допустим, есть у меня кольцевой буфер, реализованный без использования критических секций. То есть есть один владелец на чтение (тот процесс, который только читает данные из очереди) и один на запись. Каждый модифицирует свой указатель, чужой только читает (при необходимости).

С FreeR() - ясно, но как Вы реализовали FreeW() при условии отсутствия критических секций и прочих механизмов синхронизации (типа эксклюзивного доступа)? Разве это возможно? Если функция будет модифицировать указатель записи в сторону уменьшения содержимого очереди, то это нарушит функционирование такой очереди. Имхо - при таком алгоритме процесс записи может только дописывать в очередь. Удалять - не может.

 

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

Теперь, собственно вопрос: а можно ли как-то контекстно выделить функцию или кусок исходника, в котором вызов Free() автоматически компилятором заменится на вызов конкретной FreeR() или FreeW()?

Не очень понятно - зачем это нужно? Ведь всё равно один процесс будет вызывать Write(), а другой - Read(). А значит нет никакого смысла делать для них единый Free(), всё равно привязка процесса к операции имеется (посредством имён Write()/Read()). Только лишняя путаница добавится.

Я наоборот в таких случаях стараюсь задать явно различающиеся имена, типа: Write() + Flush() и Read() + Skip().

Share this post


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

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

Почему же? Вот моя реализация на Си

Скрытый текст

#ifndef _RINGBUF_H_
#define _RINGBUF_H_


#include "types.h"
#include "stddefs.h"


#define RBTYPE      u8
#define RB_BUF_SIZE (512 + 1)


typedef struct
{
  volatile s32 rp, wp;
  RBTYPE buf[RB_BUF_SIZE];
}sRingBuf;


#define rb_Init(rb) (rb)->rp = (rb)->wp = 0
s32     rb_Peek(sRingBuf *rb, RBTYPE buf[], s32 len);
s32     rb_Read(sRingBuf *rb, RBTYPE buf[], s32 len);
s32     rb_Write(sRingBuf *rb, RBTYPE buf[], s32 len);
s32     rb_Alloc(sRingBuf *rb, RBTYPE *ptr[], s32 len);
void    rb_FreeR(sRingBuf *rb, s32 len);
void    rb_FreeW(sRingBuf *rb, s32 len);
#define rb_CleanR(rb) (rb)->rp = (rb)->wp
#define rb_CleanW(rb) (rb)->wp = (rb)->rp
s32     rb_GetFree(sRingBuf *rb);
s32     rb_GetBusy(sRingBuf *rb);

#endif
Скрытый текст

#include "ringbuf.h"


s32 rb_Peek(sRingBuf *rb, RBTYPE buf[], s32 len)
{
  s32 rlen = rb_GetBusy(rb);
  if(rlen > len) rlen = len;
  len = rlen; s32 rp = rb->rp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; rp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; rp &= (rp - RB_BUF_SIZE) >> 31)
#endif
    *buf++ = rb->buf[rp++];
  return rlen;
}

s32 rb_Read(sRingBuf *rb, RBTYPE buf[], s32 len)
{
  s32 rlen = rb_GetBusy(rb);
  if(rlen > len) rlen = len;
  len = rlen; s32 rp = rb->rp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; rp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; rp &= (rp - RB_BUF_SIZE) >> 31)
#endif
    *buf++ = rb->buf[rp++];
  rb->rp = rp; return rlen;
}

s32 rb_Write(sRingBuf *rb, RBTYPE buf[], s32 len)
{
  s32 wlen = rb_GetFree(rb);
  if(wlen > len) wlen = len;
  len = wlen; s32 wp = rb->wp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; wp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; wp &= (wp - RB_BUF_SIZE) >> 31)
#endif
    rb->buf[wp++] = *buf++;
  rb->wp = wp; return wlen;
}

s32 rb_Alloc(sRingBuf *rb, RBTYPE *ptr[], s32 len)
{
  s32 alen = rb_GetFree(rb);
  if(alen > len) alen = len;
  len = alen; s32 wp = rb->wp;
#if (pwroftwo(RB_BUF_SIZE))
  for(; len-- > 0; wp &= RB_BUF_SIZE - 1)
#else
  for(; len-- > 0; wp &= (wp - RB_BUF_SIZE) >> 31)
#endif
    *ptr++ = &rb->buf[wp++];
  rb->wp = wp; return alen;
}

void rb_FreeR(sRingBuf *rb, s32 len)
{
  s32 t = rb_GetBusy(rb);
  if(t > len) t = len;
  if((t = rb->rp + t) >= RB_BUF_SIZE)
    t -= RB_BUF_SIZE;
  rb->rp = t;
}

void rb_FreeW(sRingBuf *rb, s32 len)
{
  s32 t = rb_GetBusy(rb);
  if(t > len) t = len;
  if((t = rb->wp - t) < 0)
    t += RB_BUF_SIZE;
  rb->wp = t;
}

s32 rb_GetFree(sRingBuf *rb)
{
  s32 t = rb->rp;
  if((t -= rb->wp) <= 0)
    t += RB_BUF_SIZE;
  return t - 1;
}

s32 rb_GetBusy(sRingBuf *rb)
{
  s32 t = rb->wp;
  if((t -= rb->rp) < 0)
    t += RB_BUF_SIZE;
  return t;
}


rb_Peek() - функция неразрушающего чтения. Полезна, например, при ожидании чего-то в очереди определенной длины.
rb_Alloc() - функция резервирования места в очереди для отложенного заполнения. Полезна при организации каналов обмена.
FreeR() - функция очистки желаемого количества элементов со стороны хвоста (вызывается процессом-читателем).
FreeW() - то же, что и FreeR(), только чистит со стороны головы (вызывается процессом-писателем).

FreeW() у меня вызывается, как пример, в драйвере SLIP (байт-стаффинг), когда N символов между двумя разделителями пришло больше, чем буфер может вместить (либо когда N > некого #define MAX_SLIP_FRAME_LEN). В этом случае все уже принятые символы "отбрасываются" и сообщение теряется. Все, очередь готова принимать следующий пакет.

P.S. А. Кажется я допер. Вас ввело в заблуждение назначение функции: FreeW() у меня "откусывает" именно со стороны головы (т.е. куда писали данные). Она освобождает последние занятые ячейки очереди. А вот FreeR() - самые старые. Возможно, у меня опрометчивые названия этих функций. Родились, видимо, в силу ненужности иметь способ чистки очереди со стороны хвоста (самые старые записи) процессом-писателем. Надо будет дополнить.

Share this post


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

FreeW() - то же, что и FreeR(), только чистит со стороны головы (вызывается процессом-писателем).

Теперь посмотрите внимательно на свои rb_Read() и rb_FreeW(). И подумайте: что будет если например 1-й процесс вызвал rb_Read(, 5) (когда в очереди было 6 байт), а после выполнения строчки 

s32 rlen = rb_GetBusy(rb);

(но до модификации rp внутри rb_Read()) он был прерван 2-м (более приоритетным) процессом, вызовом rb_FreeW(, 5), при размере очереди скажем байт 20?

А произойдёт у Вас нарушение когерентности указателей rp, wp, после чего получите из очереди порцию мусора.

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

Допустим, есть у меня кольцевой буфер, реализованный без использования критических секций. То есть есть один владелец на чтение (тот процесс, который только читает данные из очереди) и один на запись. Каждый модифицирует свой указатель, чужой только читает (при необходимости).

Вы не учли другое правило таких очередей: Каждый указатель должен меняться только в одну сторону. А у Вас wp меняется в недопустимую сторону (уменьшения) в rb_FreeW().

 

Добавление функционала "удаления ранее записанных последних данных" в таких очередях решается другими способами. Например: добавлением 3-го указателя. Назовём его ep (хвост готовых для чтения данных). Тогда функция записи добавляет данные от wp до rp (модифицируя wp), а функция чтения - читает от rp до ep (модифицируя rp). ep же передвигается вперёд только когда данные точно готовы для чтения (не могут быть удалены FreeW()). FreeW() собственно (из процесса записи) делает: ep=wp. Данные между rp и ep - недоступны из контекста процесса записи, только из контекста процесса чтения. Данные от ep до wp - доступны только из контекста процесса записи.

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

 

PS: А зачем Вы во всех функциях явно передаёте указатель на объект sRingBuf? Куча же лишних буков, захламляющих исходник. :umnik2: Почему бы все эти rb_Read(), rb_Write() не сделать членами sRingBuf? И указатель на объект sRingBuf будет передаваться неявно.

Share this post


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

А произойдёт у Вас нарушение когерентности указателей rp, wp, после чего получите из очереди порцию мусора.

Ну да. Хрень получилась:to_take_umbrage: Классическая гонка. Спасибо за бдительность, надо исправлять.

Цитата

PS: А зачем Вы во всех функциях явно передаёте указатель на объект sRingBuf?

Это Си-шный исходник, писал из дома уже, а переделанный на C++ лежал на работе.

P.S. Получается, и CleanW(), вызванный из процесса-писателя, может поломать логику очереди аналогично FreeW().

Share this post


Link to post
Share on other sites

Еще раз всех категорически приветствую:smile:

Что-то прифигел я маленечко со всевозможных способов инициализации разных объектов. Информация слишком разбита на кусочки; отрывочно читая про все это, не собирается общей картины. Вопрос следующий: с какой (неведомой мне пока что на данном этапе) целью комитет избавился от назначенной инициализации, как это было в C99? Это, например, struct str = {.a = 10, .b = 20}; такие конструкции. Есть какая-то логика в таком ограничении? Я полагаю, так обеспечивается сокрытие приватных данных в классах, иначе они тупо становятся доступны пользователю. Дык что теперь, я должен в такую структуру добавлять конструктор со списком инициализаторов а-ля

struct
{
  u32 a;
  u32 b;
public:
  str(u32 init_a, u32 init_b) : a(init_a), b(init_b) {}
}str;

?

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.