Простые устройства
Просто об устройствах

Класс FIFO-буффера (C++)

first-in-first-outВ статье описывается класс Fifo-буфера, позволяющий хранить в нем объекты произвольного размера.

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

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

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

Напомню, что принцип работы FIFO буфера заключается в том, что объекты помещаются и извлекаются из буфера по принципу «Первым вошел, первым вышел», т.е. чем раньше объект добавлен в буфер, тем быстрее он будет из него извлечен.

Листинг класса приведен ниже

{code}#ifndef FIFO_H_
#define FIFO_H_

template <unsigned int Tsize>
class CFifoBuffer
{
private:
   // -------- Данные -------- //
   // Буфер хранения данных
   unsigned char Data[Tsize];
   // Переменная хранения адреса выборки
   unsigned int Addr;
   // Переменная хранения задействованного объема памяти
   unsigned int Size;

public:
   // Метод добавления данных в буфер
   unsigned char Add(unsigned char *pSavaData, unsigned char SaveSize)
   {
      unsigned int AddrTemp;

      if(((Tsize - Size) < (SaveSize + 1)) || (SaveSize == 0)) return 0;

      AddrTemp = Addr + Size;
      if(AddrTemp >= Tsize) {AddrTemp -= Tsize; }
      Data[AddrTemp] = SaveSize;
      AddrTemp++;

      for(unsigned int i = 0; i < SaveSize; i++, AddrTemp++)
      {
         if(AddrTemp >= Tsize) {AddrTemp -= Tsize;}
         Data[AddrTemp] = pSavaData[i];
      }

      Size += SaveSize + 1;
      return 1;
   }

   // Метод извлечения данных из буфера
   unsigned int Cut(unsigned char *pData)
   {
      unsigned char SizeTemp;
      unsigned int AddrTemp = Addr;

      if(Size == 0) return 0;

      SizeTemp = Data[Addr] - 1;
      if(SizeTemp == 0) return 0;

      AddrTemp++;

      for(unsigned int i = 0; i <= SizeTemp; i++, AddrTemp++)
      {
         if(AddrTemp >= Tsize) {AddrTemp -= Tsize; }
         pData[i] = Data[AddrTemp];
      }
      SizeTemp += 2;
      Size -= SizeTemp;
      Addr = AddrTemp;
      if(Addr >= Tsize) {Addr -= Tsize; }

      return SizeTemp - 1;
   }

   // Конструктор
   CFifoBuffer(void)
   {
      Addr = 0;
      Size = 0;
   }
};

#endif{/code}

Поскольку класс является шаблонным, то тело методов приведено непосредственно в h-файле.

Краткое описание данных класса приведено непосредственно в листинге и дополнительных комментариев не требует.

Для добавления и извлечения данных из буфера используются методы Add и Cut соответственно.

Метод Add добавляет SaveSize байт буфера pSaveData в буфер Data, метод возвращает результат добавления данных в буфер:

0 – если данные не были добавлены в буфер;

1 –если данные в буфер добавлены.

Метод Cut извлекает из буфера Data объект и помещает его в буфер pData, метод возвращает размер извлеченного объекта в байтах.

При объявлении объектов класса необходимо указать размер буфера в байта.

Пример объявления объекта

{code}CFifoBuffer<200> ExBufRx;{/code}

где 200 - размер буфера в байтах

Не смотря на то, что методы работают с массивами типа unsigned char, класс позволяет хранить объекты любого типа при этом для необходимо заведомо знать тип сохраняемых объектов, а если тип объекта неизвестен, то вводить дополнительный байт-идентификатор.

Пример добавления и извлечения различных типов данных в буфер приведен ниже. Чтобы не создавать несколько примеров для каждого типа данных, я в одном примере показал добавление объектов различных типов, чего в реальных проектах делать не рекомендую.

{code}#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include "fifo.h"

// Объявляем структуру для демонстрации возможнослей класса
struct STemp
{
    int AnyData;
    char str[8];
};

int main(void)
{
    // Объявляем объект класса с буфером 20 байт
    CFifoBuffer <20> MyBuf;

    int iData = 1234567;
    float fData = 34.5;
    STemp MyStruct;

    MyStruct.AnyData = 98765;
    strcpy(MyStruct.str, "Test");

    // Добавляем объекты в FIFO буфер
    if(MyBuf.Add((unsigned char *)&iData, sizeof(iData)))
    {
       printf("Object iData add FIFO\n\r");
    }
    else
    {
       printf("Object iData don`t add FIFO\n\r");
    }

    if(MyBuf.Add((unsigned char *)&fData, sizeof(fData)))
    {
       printf("Object fData add FIFO\n\r");
    }
    else
    {
       printf("Object fData don`t add FIFO\n\r");
    }

    // Объект MyStruct не будет добавлен т.к. буфер переполнен
    if(MyBuf.Add((unsigned char *)&MyStruct, sizeof(MyStruct)))
    {
       printf("Object MyStruct add FIFO\n\r");
    }
    else
    {
       printf("Object MyStruct don`t add FIFO\n\r");
    }

    printf("\n\r\n\r");

    // Извлекаем объект iData из буфера
    int iTemp = 0;
    if(MyBuf.Cut((unsigned char *)&iTemp))
    {
       printf("Object iData = %d\n\r", iTemp);
    }
    else
    {
       printf("Object iData don`t cut\n\t");
    }

    // Добавляем объект MyStruct
    if(MyBuf.Add((unsigned char *)&MyStruct, sizeof(MyStruct)))
    {
       printf("Object MyStruct add FIFO\n\r");
    }
    else
    {
       printf("Object MyStruct don`t add FIFO\n\r");
    }

    // Извлекаем объекты из буфера
    float fTemp = 0;
    if(MyBuf.Cut((unsigned char *)&fTemp))
    {
       printf("Object fData = %f\n\r", fTemp);
    }
    else
    {
       printf("Object fData don`t cut\n\t");
    }

    STemp MyStructTemp;
    if(MyBuf.Cut((unsigned char *)&MyStructTemp))
    {
       printf("Object MyStruct.AnyData = %d\n\r", MyStructTemp.AnyData);
       printf("Object MyStruct.str = %s\n\r", MyStructTemp.str);
    }
    else
    {
       printf("Object fData don`t cut\n\r");
    }

    return 0;
}{/code}

Результат выполнения примера приведен на рисунке.

Результат работы программыВ завершении обращаю внимание, что при объявлении в одном и том же проекте объектов класса с различным объемом буфера в памяти программ компилятором будет создано соответствующее количество копий методов т.е. для работы с буфером размера N1 будут использоваться одни методы, для работы с буфером размера N2 другие.

Комментарии   

+3 #1 Артём 17.07.2014 16:56
volatile не хватает для Size, Addr и Data.
-1 #2 molchec 22.07.2014 21:27
Артем, поясните пожалуйста какой эффект по Вашему мнению даст ключевое слово volatile для закрытых элементов класса ?
Закрытые переменные могут изменяться только методами класса, на то они и закрытые.
Обращаю внимание, что ключевое слово volatile это не что иное как просьба программиста к компилятору не оптимизировать действия с данной переменной и в случае с встроенными системами может быть воспринята оптимизатором весьма неоднозначно :-)
+3 #3 ARV 25.07.2014 01:40
вообще-то, volatile должно восприниматься исключительно однозначно :)
-1 #4 molchec 27.07.2014 12:21
ARV да в том то им дело, что должно.
Хотел обратить внимание, что агрессивные оптимизаторы иногда его игнорируют.

Добавить комментарий

Защитный код
Обновить

Обсудить эту статью на форуме (0 ответов).

Copyright 2019 © simple-devices.ru.
При использовании материалов ссылка на simple-devices.ru обязательна.