LINUX.ORG.RU

Красивая реализация списков


0

2

И так, после некоторых экспериментов я пришел к следующей реализации списков, которая включает в себя все лучшее из стандартной реализации и реализации «как в линуксе» :)

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

В настоящий момент в каждую сруктуру, которая может включаться в список нужно добавить служебный элемент для связи (макрос LIST_LINK), который всегда называется link. Если во все макросы добавить название служебного поля как параметр, то можно иметь один и то-же элемент в разных списках (как в линуксе), мне это не нужно, поэтому не делал.

Конструктивная критика приветствуется.

Сама реализация:

#ifndef _LIST_H_

#define _LIST_H_


#define LIST_LINK(type) \
  struct { struct type *next;  struct type *prev; } link


#define DECLARE_LIST(type, name) \
  type name = { .link = { .next = &(name), .prev = &(name) } }


#define LIST_INIT(ptr) \
  do { (ptr)->link.next = ptr; (ptr)->link.prev = ptr; } while (0)


#define list_add_after(_item, _new) \
  do { \
    (_new)->link.next = (_item)->link.next; \
    (_new)->link.prev = (_item); \
    (_item)->link.next->link.prev = (_new); \
    (_item)->link.next = (_new); \
  } while (0)

#define list_add_before(_item, _new) \
  do { \
    (_new)->link.next = (_item); \
    (_new)->link.prev = (_item)->link.prev; \
    (_item)->link.prev->link.next = (_new); \
    (_item)->link.prev = (_new); \
  } while (0)

#define list_add(_list, _new) \
  list_add_before(_list, _new)

#define list_add_head(_list, _new) \
  list_add_after(_list, _new)

#define list_del(_item) \
  do { \
    (_item)->link.prev->link.next = (_item)->link.next; \
    (_item)->link.next->link.prev = (_item)->link.prev; \
    (_item)->link.next = NULL; \
    (_item)->link.prev = NULL; \
  } while (0)

#define list_del_init(_list) \
  do { \
    list_del(_item); \
    LIST_INIT(_item); \
  } while (0)

#define list_is_empty(_list) \
  ((_list)->link.next == (_list))


#define list_foreach(_var, _list) \
  for (_var = (_list)->link.next; _var != (_list); _var = _var->link.next)

#define list_foreach_back(_var, _list) \
  for (_var = (_list)->link.prev; _var != (_list); _var = _var->link.prev)

#define list_foreach_var(_type, _var, _list) \
  for (_type *_var = (_list)->link.next; _var != (_list); _var = _var->link.next)

#define list_foreach_back_var(_type, _var, _list) \
  for (_type *_var = (_list)->link.prev; _var != (_list); _var = _var->link.prev)

#define list_foreach_safe(_var, _list, _tmp) \
  for (_var = (_list)->link.next, _tmp = (_var)->link.next; _var != (_list); \
       _var = _tmp, _tmp = (_var)->link.next)

#define list_foreach_back_safe(_var, _list, _tmp) \
  for (_var = (_list)->link.prev, _tmp = (_var)->link.prev; _var != (_list); \
       _var = _tmp, _tmp = (_var)->link.prev)

#endif // _LIST_H_


Пример использования:

#include <stdio.h>
#include <stdlib.h>
#include "list.h"

typedef struct num_t
{
  int      num;
  LIST_LINK(num_t);
} num_t;

static DECLARE_LIST(num_t, nums);

int main(void)
{
  num_t n1, n2, n3, n4;
  num_t *n5;
  num_t *ext_var;

  n1.num = 1111;
  n2.num = 2222;
  n3.num = 3333;
  n4.num = 4444;

  n5 = malloc(sizeof(num_t));
  n5->num = 5555;

  list_add(&nums, &n1);
  list_add(&nums, &n2);
  list_add(&nums, &n3);
  list_add(&nums, &n4);
  list_add(&nums, n5);

  list_del(&n2);

  printf("Go through list using c99 style for loop declaration:\n");
  list_foreach_var (num_t, var, &nums)
  {
    printf("  %d\n", var->num);
  }

  printf("Go through list using external variable as loop parameter:\n");
  list_foreach (ext_var, &nums)
  {
    printf("  %d\n", ext_var->num);
  }

  return 0;
}

★★★★
Ответ на: комментарий от idle

наследование тут как раз самое подходящее средство (хотя, конечно, в языке типа скалы или хаскеля может можно еще круче)

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

если прям это единственный недостаток, щас изображу

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от idle

кстати, а что это?

http://ru.wikipedia.org/wiki/Интрузивный_список

template<class X> struct MaxValuePlus1 { /// надеюсь оно реализуемо, но искать влом
    static const int value=2; /// а пока что вот такой чит
};

enum TrivialEnum { ZERO };
template<> struct MaxValuePlus1<TrivialEnum> { static const int value=1; };

template<class Payload, class Chains=TrivialEnum> class IntrusiveList
{
public:
    Payload* add(Chains chain, Payload* list) { next_[chain]=list; return next_[chain]; }
    Payload* add(Payload* list) { return add((Chains)0, list); }
    Payload* next(Chains chain=(Chains)0) { return next_[chain]; }
protected:
    IntrusiveList() {
        for(int i=0; i<MaxValuePlus1<Chains>::value; i++)
            next_[i]=0;
    }    /// нефиг создавать "просто" IntrusiveList без Payload-а
private:
    Payload* next_[MaxValuePlus1<Chains>::value];
};

#define for_each2( element, list ) for( typeof(list)    element=list; element!=NULL; element=element->next() )
#define for_each3( element, list, chain ) for( typeof(list)    element=list; element!=NULL; element=element->next(chain) )

/// usage:
enum Chains { CHAIN1,  CHAIN2 };  // накрайняк придется вместо этого писать DEFINE_CHAINS(Chains, CHAIN1,  CHAIN2)

struct SomeStruct;
struct SomeStruct: public IntrusiveList<SomeStruct, Chains>
{
    int value;
    SomeStruct(int value): value(value) {}
};

#include <iostream>

int main()
{
    int n=3;
    SomeStruct *ss[n];
    SomeStruct* list1=new SomeStruct(0);
    SomeStruct* list2=new SomeStruct(n+1);
    for(int i=0; i<n; i++)
        ss[i] = new SomeStruct(i+1);
    list1->add(CHAIN1, ss[0])->add(CHAIN1, ss[1])->add(ss[2]);
    list2->add(CHAIN2, ss[2])->add(CHAIN2, ss[1])->add(CHAIN2, ss[0]);

    for_each2( element, list1 )
        std::cout << element->value << ' ' << element << ' ' << element->next() << '\n';
    for_each3( element, list2, CHAIN2 )
        std::cout << element->value << ' ' << element << ' ' << element->next(CHAIN2) << '\n';
    return 0;
}
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> http://ru.wikipedia.org/wiki/Интрузивный_список

а. если бы там не было примера, я бы ни хрена не понял.
определение там просто замечательное.

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

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

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

без обид, это просто мое мнение. feel free to disagree ;)

idle ★★★★★
()
Ответ на: комментарий от idle

> feel free to disagree

гы!

было бы с чем не соглашаться, я бы конечно не согласился

а пока я вижу только «код мне не нравится, но почему — не скажу, даже не проси»

в русской википедии и правда косноязычно написано, но понять можно

ладно, тогда займусь конструктивной критикой ТС, что-ли

www_linux_org_ru ★★★★★
()

облом будет в случае

list_add_after( some_item, function_that_allocates_and_returns_new_node() );

или

while( i<n ) {
  ...
  list_add_after( some_item, function(++i) );
  ...
}

если уж пытаешься изображать функции макросами, то капслочь их, либо юзай жцц-шные расширения typeof и возможно ({ })

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

облом будет в случае

Да, на это я нарвался уже.

alexru ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.