OberonCore
https://forum.oberoncore.ru/

реализация очереди-списка на C++
https://forum.oberoncore.ru/viewtopic.php?f=27&t=2241
Страница 1 из 1

Автор:  Созерцатель [ Среда, 20 Январь, 2010 00:12 ]
Заголовок сообщения:  реализация очереди-списка на C++

Отделено отсюда: viewtopic.php?p=40457#p40457
Извините за длинное сообщение.

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

Код:
//----------------------------------------------------------------------------
class Queue
{
public:

   typedef void(FreeObjectFunc_t) ( void* object );
   typedef bool(FindIfCheckFunc_t)( void* object, void *data );
   typedef void(ForEachFunc_t)    ( void* object, void *data );

private:

   struct QueueItem
   {
      QueueItem *prev_, *next_;
      void*      object_;
      QueueItem( QueueItem *prev = NULL, QueueItem *next = NULL, void* object = NULL )
      : prev_( prev ), next_( next ), object_( object )
      { };
   };

   QueueItem        *freeItems_;
   QueueItem        *items_;
   QueueItem        *last_;
   int               count_;
   int               capacityDelta_;
   FreeObjectFunc_t *pFreeObjectFunc_;

   static void FreeQueueItems( QueueItem* *items, FreeObjectFunc_t *pFreeObjectFunc );
   void InnerRemove( QueueItem* item );
   void AddToFree  ( QueueItem* item );
   void FreeItem   ( QueueItem* item );
   void MakeFirst  ( QueueItem* item );

public:
   //---------------------------------------------------------------------------
    Queue( FreeObjectFunc_t *pFreeObjectFunc = NULL, int startCapacity = 16, int capacityDelta = 4 );
   ~Queue();
   //---------------------------------------------------------------------------
   int   Count ();
   bool  Put   ( void* object );
   void* Front ();
   void* Get   ();
   bool  Remove( void* object );
   //---------------------------------------------------------------------------
   bool  Has    ( void* object, bool makeFirst = false );
   void* FindIf ( FindIfCheckFunc_t *pCheckFunc,   void *data, bool makeFirst = false );
   void  ForEach( ForEachFunc_t     *pForEachFunc, void *data = NULL );
   //---------------------------------------------------------------------------
};
//----------------------------------------------------------------------------


//---------------------------------------------------------------------------
Queue::Queue( FreeObjectFunc_t *pFreeObjectFunc, int startCapacity, int capacityDelta )
: freeItems_      ( NULL ),
  items_          ( NULL ),
  last_           ( NULL ),
  count_          ( 0 ),
  capacityDelta_  ( capacityDelta ),
  pFreeObjectFunc_( pFreeObjectFunc )
{
   while( startCapacity-- )
   {
      QueueItem *item;
      try{ item = new QueueItem( NULL, freeItems_, NULL ); }catch(...){ item = NULL; }
      if( ! item  )
         throw -1;
      freeItems_ = item;
   }
}

//---------------------------------------------------------------------------

   void Queue::FreeQueueItems( QueueItem* *items, FreeObjectFunc_t *pFreeObjectFunc )
   {
      while( *items )
      {
         QueueItem *item = *items; *items = item->next_;
         if( pFreeObjectFunc && item->object_ )
            (*pFreeObjectFunc)( item->object_ );
         delete item;
      }
   }

Queue::~Queue()
{
   FreeQueueItems( &items_,     pFreeObjectFunc_ );
   FreeQueueItems( &freeItems_, NULL );
}

//---------------------------------------------------------------------------
int Queue::Count()
{
   return count_;
}
//---------------------------------------------------------------------------
bool Queue::Put( void* object )
{
   QueueItem *item = freeItems_;
   if( item )
      freeItems_ = item->next_;
   else
   {
      try{ item = new QueueItem(); }catch(...){ item = NULL; }
      if( ! item )
         return false;
      if( (capacityDelta_-1) > 0 )
         for( int i = 1; i < capacityDelta_; ++i )
         {
            QueueItem *freeItem;
            try{ freeItem = new QueueItem( NULL, freeItems_, NULL ); }catch(...){ freeItem = NULL; }
            if( ! freeItem )
               break;
            freeItems_ = freeItem;
         }
   }
   *item = QueueItem( last_, NULL, object );
   if( last_ )
      last_->next_ = item;
   else
      items_ = item;
   last_ = item;

   ++count_;

   return true;
}
//---------------------------------------------------------------------------
void* Queue::Front()
{
   return (items_) ? items_->object_ : NULL ;
}


   void Queue::InnerRemove( QueueItem* item )
   {
      if( item->prev_ )
         item->prev_->next_ = item->next_;
      else
         items_ = item->next_;

      if( item->next_ )
         item->next_->prev_ = item->prev_;
      else
         last_ = item->prev_;

      item->next_ = item->prev_ = NULL;
   }

   void Queue::AddToFree( QueueItem* item )
   {
      item->next_ = freeItems_;
      freeItems_ = item;
   }

   void Queue::FreeItem( QueueItem* item )
   {
      InnerRemove( item );
      AddToFree( item );
      --count_;
   }

   void Queue::MakeFirst( QueueItem* item )
   {
      InnerRemove( item );
      item->next_ = items_;
      items_ = item;
   }



//---------------------------------------------------------------------------
void* Queue::Get()
{
   QueueItem *item = items_; if( ! item ) return NULL;
   FreeItem( item );
   return item->object_;
}
//---------------------------------------------------------------------------
bool Queue::Remove( void* object )
{
   bool res;
   QueueItem *item = items_;
   for( ; item && item->object_ != object ; item = item->next_ ) /* !!! */ ;
   if( (res = (item != NULL)) )
      FreeItem( item );
   return res;
}


//---------------------------------------------------------------------------
void* Queue::FindIf( FindIfCheckFunc_t *pCheckFunc, void *data, bool makeFirst )
{
   void* object;
   if( ! pCheckFunc )
      object = NULL;
   else
   {
      QueueItem *item = items_;
      for( ; item && !(*pCheckFunc)( item->object_, data ) ; item = item->next_ ) /* !!! */ ;
      if( ! item )
         object = NULL;
      else
      {
         object = item->object_;
         if( makeFirst && item != items_ )
            MakeFirst( item );
      }
   }
   return object;
}

//---------------------------------------------------------------------------
bool Queue::Has( void* object, bool makeFirst )
{
   QueueItem *item = items_;
   for( ; item && (item->object_ != object) ; item = item->next_ ) /* !!! */ ;
   if( ! item )
      return false;
   if( makeFirst && item != items_ )
      MakeFirst( item );
   return true;
}

//-----------------------------------------------------------------------------
void Queue::ForEach( ForEachFunc_t *pForEachFunc, void *data )
{
   if( pForEachFunc )
   {
      for( QueueItem *item = items_; item ; item = item->next_ )
         (*pForEachFunc)( item->object_, data );
   }
}

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 00:19 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Тут есть отлично известная проблема с void*. Типизацию-то теряем.

В КП можно и не терять. :)
Подсказка к такому подходу давалась:

viewtopic.php?f=2&t=2006

Автор:  Созерцатель [ Среда, 20 Январь, 2010 00:25 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Илья Ермаков писал(а):
Подсказка к такому подходу давалась:
viewtopic.php?f=2&t=2006

Извините, я там не понял почти ничего...
Можете вкратце идею объяснить здесь?

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 00:31 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Если Вы имеете:
1) абстракцию двоичного потока (одинаковую и для внешней, и для оперативной памяти, и для любой другой реализации);
2) возможность метапрограммирования в языке (т.е. разбора типа RECORD-а и его свойств);
3) нормальные стековые RECORD (потому что в той же Жабе их нет),

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

Библиотечный file of record, только на новом уровне, с абстракцией.

Автор:  Созерцатель [ Среда, 20 Январь, 2010 00:33 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

А разве реализация абстракции потока и разбор метаданных не будут лишние такты забирать?

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 00:35 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Будут. Но это слагаемые, что вполне терпимо.

Главное, что не множители и степени, как в случае Жабы-Шарпа, в которых нормальное системное программирование вообще затруднено, без нагрузки на сборщик мусора на каждый чих и т.п.

Автор:  Созерцатель [ Среда, 20 Январь, 2010 00:36 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Да и не вижу я особой опасности с моей очередью делать
а = (AnyType*)queue.Get();
когда я знаю точно, что у меня в этой очереди-списке только AnyType + потомки.

Автор:  Сергей Губанов [ Среда, 20 Январь, 2010 13:00 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Созерцатель писал(а):
а = (AnyType*)queue.Get();
В Вашем контейнере лежат указатели на объекты, а Илья говорит про ситуацию когда в контейнере лежат не указатели на объекты, а сами объекты.

Автор:  Созерцатель [ Среда, 20 Январь, 2010 13:10 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Сергей Губанов писал(а):
Созерцатель писал(а):
а = (AnyType*)queue.Get();
В Вашем контейнере лежат указатели на объекты, а Илья говорит про ситуацию когда в контейнере лежат не указатели на объекты, а сами объекты.
С течением времени данный вариант стал наимение логически обоснованным для использования. С указателями быстрее работать-копировать. Кроме того, из объектов были выброшены отдельные поля, отвечающие за их состояние. Вместо этого объекты (ссылки-указатели на них) переходят из одной очереди-списка в другую. Под охраной критических секций.
Со временем вариант с непосредственным хранением объектов в коллекциях вообще вышел из нашей практики. Только ссылки-указатели.

Автор:  Валерий Лаптев [ Среда, 20 Январь, 2010 19:43 ]
Заголовок сообщения:  Re: реализация очереди-списка на C#

Еще я не понял, при чем здесь Додиез, то есть C#. Это ж чистый С++!

Автор:  Евгений Темиргалеев [ Среда, 20 Январь, 2010 20:04 ]
Заголовок сообщения:  Re: реализация очереди-списка на C++

Настолько отвык, что беглый взгляд попутал... В мозгу идёт сборка мусора :mrgreen:

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 22:50 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Созерцатель писал(а):
С указателями быстрее работать-копировать.
..
Со временем вариант с непосредственным хранением объектов в коллекциях вообще вышел из нашей практики.


Зато при хранении по значению можно не различать оперативную и внешнюю память.
Ссылочные же структуры строятся по индексу. Эквивалент указателя - пара (КОНТЕЙНЕР, ИНДЕКС). Плюсы по сравнению с указателями, кроме прозрачности: безопасность при явном выделении-освобождении памяти.

В Композите (viewforum.php?f=21), кстати, "pointer-free programming" сделано именно по такой схеме - введены хорошие ассоциативные разрежённые массивы (хотя мне не нравится встройка решения в язык). Ещё на СУБД Cache у них похоже.

Автор:  Созерцатель [ Среда, 20 Январь, 2010 22:54 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Илья Ермаков писал(а):
Эквивалент указателя - пара (КОНТЕЙНЕР, ИНДЕКС)
ЧТо в данном кортеже есть КОНТЕЙНЕР? Тип его значения какой?

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 22:58 ]
Заголовок сообщения:  Re: реализация очереди-списка на C++

В программе - объект обычный (в КП - POINTER TO RECORD).

Но можно целое число - код типа записей, на которые ссылаемся ("номер таблицы БД").

На самом же деле, если у Вас статически известно, какого типа ссылка, то можно просто LONGINT, сделанный как подтип целого (обёрнутый в RECORD: TYPE PersonLink = RECORD id: LONGINT END). Это получается, в терминах БД, внешний ключ (foreign key). Первичный ключ (Primary key) - индекс в контейнере.

Автор:  Созерцатель [ Среда, 20 Январь, 2010 23:19 ]
Заголовок сообщения:  Re: реализация очереди-списка на C++

Илья Ермаков писал(а):
В программе - объект обычный (в КП - POINTER TO RECORD).
Но можно целое число - код типа записей, на которые ссылаемся ("номер таблицы БД").
Что тогда будет означать "удаление объекта"? Обнуление всей записи? Изменение какого-то поля внутри объекта?
Это что В КАЖДОМ объекте (вне зависимости от типа) иметь поле "актуальность", "активность", "живой"?
Илья Ермаков писал(а):
На самом же деле, если у Вас статически известно, какого типа ссылка, то можно просто LONGINT, сделанный как подтип целого (обёрнутый в RECORD: TYPE PersonLink = RECORD id: LONGINT END). Это получается, в терминах БД, внешний ключ (foreign key). Первичный ключ (Primary key) - индекс в контейнере.
Понятийная база у нас разная. Нам придётся писать код преобразования идентификатора объекта, опирающегося на значение множества адресов памяти и операций с ними в чуждый задаче понятийный аппарат реляционных БД. Нам это далеко не всегда нужно и выгодно.
Вы плодите лишние сущности. Выгоды от этого я не вижу.

Автор:  Илья Ермаков [ Среда, 20 Январь, 2010 23:35 ]
Заголовок сообщения:  Re: реализация очереди-списка на C++

Так у нас задачи разные :)

Мы, фактически, этим заменяем БД и делаем перманентность.

Кроме того, простой File (при реализации в памяти) и ReadRec/WriteRec используется для работы с двоичными данными в тех же сетевых компонентах. Т.е. большинство системных модулей оказывается без использования IMPORT SYSTEM, языковыми безопасными средствами (по эффективности практически прямой доступ, со слагаемым на вызов абстрактных методов интерфейсa File).

Если сравнивать с тем же Go... То слайсы мы такие используем. Поэтому, когда Go опубликовали, у меня сразу большое дежа-вю: "так всё это я уже видел..." у себя же :)

Автор:  Валерий Лаптев [ Четверг, 21 Январь, 2010 14:36 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Созерцатель писал(а):
Со временем вариант с непосредственным хранением объектов в коллекциях вообще вышел из нашей практики. Только ссылки-указатели.

ИМХО, два разных выхода (этот и у Ильи) - это следствие двух разных подходов при создании основ. То есть ББ+КП и С++ с любой средой.

Автор:  Созерцатель [ Четверг, 21 Январь, 2010 15:06 ]
Заголовок сообщения:  Re: Правильное повторное (не)использование

Валерий Лаптев писал(а):
Созерцатель писал(а):
Со временем вариант с непосредственным хранением объектов в коллекциях вообще вышел из нашей практики. Только ссылки-указатели.
ИМХО, два разных выхода (этот и у Ильи) - это следствие двух разных подходов при создании основ. То есть ББ+КП и С++ с любой средой.
Вы не правы.

Страница 1 из 1 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/