Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 09:58
Оценка: :)))
Раньше на raw-ptr узел дерева, если говорить о двоичном варианте, выглядел
struct TNode
{
   TAtrributes m_Atrributes;

   TNode* m_pLeftChild;
   TNode* m_pRightChild;

   TNode* m_pParent;
};



При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?

p.s. T4r4sB, это же вы, по-моему, недавно говорили, что raw-ptr устарели
Re: Реализация дерева средствами с++11
От: Stanislav V. Zudin Россия  
Дата: 16.04.16 12:01
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Раньше на raw-ptr узел дерева, если говорить о двоичном варианте, выглядел


__>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


Предлагаю "ход конём". Заменить указатели на индексы в массиве.

    enum { NONE = -1 };

    typedef int NODE;                   //!< Узел дерева
    typedef int ATTR;                   //!< Атрибут узла

    class Document
    {
        ... ...

        struct NodeInfo
        {
            NAMEREF name;               // Ссылка на имя
            NODE next,                  // Следующий
                 prev,                  // Предыдущий
                 parent,                // Родительский узел
                 child;                 // Первый дочерний узел
            ATTR attr;                  // Первый атрибут узла
            VALREF value;               // Ссылка на значение

            NodeInfo()
                : name(NONE)
                , next(NONE), prev(NONE)
                , parent(NONE), child(NONE)
                , attr(NONE), value(NONE) {}
        };

        typedef std::vector<NodeInfo> TNodes;
        // ------------------------------------------------------------------
        NODE m_RootNode;                // Корневой узел
        NODE m_NodeFreeList;            // Список удаленных узлов

        TNodes m_InfoNode;              // Свойства узлов

        ... ...
    };



Для навигации можно использовать итератор:

    class inode
    {
    public:
        inode();
        inode(Document* pdoc, NODE n);
        inode(const inode& n);

        LPCTSTR getName() const;
        LPCTSTR getValue() const;

        void setValue(LPCTSTR valN);
        void setValue(LPCTSTR valN, size_t length);

        iattr getAttrs() const;
        iattr getAttr(LPCTSTR name) const;

        inode getChild() const;
        inode getChild(LPCTSTR name) const;
        inode getParent() const;
        inode getNext() const;
        inode getPrev() const;

        const inode& operator++();
        bool end() const;

        NODE id() const;

        //! Добавить дочерний узел после <prev>
        inode addNode(LPCWSTR name, NODE prev);

        //! Задать значение атрибута
        iattr setAttr(LPCWSTR name, LPCWSTR val);

    protected:
        Document* m_pdoc;
        NODE m_id;
    };


Забесплатно получаем дешевую бинарную сериализацию.
_____________________
С уважением,
Stanislav V. Zudin
Re[2]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 12:31
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Здравствуйте, _hum_, Вы писали:


__>>Раньше на raw-ptr узел дерева, если говорить о двоичном варианте, выглядел


__>>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


SVZ>Предлагаю "ход конём". Заменить указатели на индексы в массиве.


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

SVZ>Забесплатно получаем дешевую бинарную сериализацию.


да, именно из-за нее вся возня со смартами (cereal не умеет выполнять сериализацию для raw-ptr — только для смартов)
Re: Реализация дерева средствами с++11
От: Pzz Россия https://github.com/alexpevzner
Дата: 16.04.16 12:43
Оценка:
Здравствуйте, _hum_, Вы писали:

__>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


Понятия не имею, как какие указатели в вашем волшебном языке называются, но по логике вещей, родитель владеет детьми, но не наоборот. Т.е., указатели "вниз" и "вверх" должны быть разными.
Re[2]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 12:48
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Здравствуйте, _hum_, Вы писали:


__>>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


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


так точно. в этом и проблема.
Re[3]: Реализация дерева средствами с++11
От: Stanislav V. Zudin Россия  
Дата: 16.04.16 12:57
Оценка:
Здравствуйте, _hum_, Вы писали:

SVZ>>Предлагаю "ход конём". Заменить указатели на индексы в массиве.


__>ну, тогда ж придется писать аналог менеждера кучи (чтоб искал свободное место, куда можно вставлять новые элементы, чтобы помечал удаленные как свободные места и т.п.). но еще чуть-чуть, и я психану и все же наверное так и сделаю, потому что тратить столько времени на реализацию обычного дерева — это уж слишком (а я уже третий день с этим вожусь)


Ох уж эта молодёжь...

Вот получение узла из "менеджера кучи":

// ==========================================================================
// Создать узел
dom::NODE dom::Document::makeNode(LPCTSTR name)
{
    dom::NODE nodeN = m_NodeFreeList;
    if ( nodeN < 0 )
    {
        nodeN = static_cast<dom::NODE>(m_InfoNode.size());
        m_InfoNode.push_back( NodeInfo() );
    }
    else
    {
        m_NodeFreeList = m_InfoNode[nodeN].next;
        m_InfoNode[nodeN] = NodeInfo();
    }

    NAMEREF nref = makeNodeName(name);
    m_InfoNode[nodeN].name = nref;

    return nodeN;
}


Удаленные узлы помещаются в односвязный список, голова которого хранится в m_NodeFreeList.
Хранить можно и плоский список, и целые поддеревья (только в makeNode придется этот факт учитывать), в общем пространство для маневра обширное.

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

По сложности — задачка на пару часов для студента 2 курса.
_____________________
С уважением,
Stanislav V. Zudin
Re[3]: Реализация дерева средствами с++11
От: Pzz Россия https://github.com/alexpevzner
Дата: 16.04.16 12:58
Оценка:
Здравствуйте, _hum_, Вы писали:

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


__>так точно. в этом и проблема.


Ну а почему бы не использовать для них разные типы?
Re: Реализация дерева средствами с++11
От: T4r4sB Россия  
Дата: 16.04.16 13:00
Оценка:
Здравствуйте, _hum_, Вы писали:

__>p.s. T4r4sB, это же вы, по-моему, недавно говорили, что raw-ptr устарели


Ну если ты пишешь контейнер, в котором сам управляешь памятью, то таки придётся с ними повозиться.
Для дерева достаточно юников на потомков и сырого на родителя. Да, сырого на родителя. Да, если он не будет владеть и его время жизни меньше, чем у указуемого объекта, то можно и сырой, ничего не случится.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: Реализация дерева средствами с++11
От: PM  
Дата: 16.04.16 13:11
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Раньше на raw-ptr узел дерева, если говорить о двоичном варианте, выглядел

__>
__>struct TNode
__>{
__>   TAtrributes m_Atrributes;

__>   TNode* m_pLeftChild;
__>   TNode* m_pRightChild;

__>   TNode* m_pParent;
__>};
__>



__>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


__>p.s. T4r4sB, это же вы, по-моему, недавно говорили, что raw-ptr устарели


В N3840: A Proposal for the World’s Dumbest Smart Pointer, v3 описаны 3 варианта для таких случаев:

1) использовать как и раньше сырой указатель; постулировать, что сырые указатели не владеют памятью, на которую указывают

2) использовать [boost|std]::optional<TNode>

3) использовать std::unique_ptr<TNode> с do nothing deleter:
struct do_nothing
{
  template <class T>
  void operator ()(T*) { }; // do nothing
};
 
template <class T>
  using non_owning_ptr = unique_ptr<T, do_nothing>;
Re[4]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:14
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Здравствуйте, _hum_, Вы писали:


SVZ>>>Предлагаю "ход конём". Заменить указатели на индексы в массиве.


__>>ну, тогда ж придется писать аналог менеждера кучи (чтоб искал свободное место, куда можно вставлять новые элементы, чтобы помечал удаленные как свободные места и т.п.). но еще чуть-чуть, и я психану и все же наверное так и сделаю, потому что тратить столько времени на реализацию обычного дерева — это уж слишком (а я уже третий день с этим вожусь)


SVZ>Ох уж эта молодёжь...


SVZ>Вот получение узла из "менеджера кучи":


  code
SVZ>
SVZ>// ==========================================================================
SVZ>// Создать узел
SVZ>dom::NODE dom::Document::makeNode(LPCTSTR name)
SVZ>{
SVZ>    dom::NODE nodeN = m_NodeFreeList;
SVZ>    if ( nodeN < 0 )
SVZ>    {
SVZ>        nodeN = static_cast<dom::NODE>(m_InfoNode.size());
SVZ>        m_InfoNode.push_back( NodeInfo() );
SVZ>    }
SVZ>    else
SVZ>    {
SVZ>        m_NodeFreeList = m_InfoNode[nodeN].next;
SVZ>        m_InfoNode[nodeN] = NodeInfo();
SVZ>    }

SVZ>    NAMEREF nref = makeNodeName(name);
SVZ>    m_InfoNode[nodeN].name = nref;

SVZ>    return nodeN;
SVZ>}
SVZ>

SVZ>Удаленные узлы помещаются в односвязный список, голова которого хранится в m_NodeFreeList.
SVZ>Хранить можно и плоский список, и целые поддеревья (только в makeNode придется этот факт учитывать), в общем пространство для маневра обширное.

SVZ>Свой код не привожу, потому что у меня там хитрое владение именами узлов — не думаю, что тебе это пригодится.


SVZ>По сложности — задачка на пару часов для студента 2 курса.


я уже все это писал раньше (я не молодежь), потому-то мне и лень было заново этим заниматься думал, с новым стандартом все это донельзя упростится — фигушки.
(и там же нюансы полезут с тем, что делать, если у сохраняемого типа нет конструктора по умолчанию — придется этими всеми списками инициализации и прочей мудатеньню заниматься.к тому же придется решать вопросы, которые больше всего не люблю — организационные — какое хранилище выбрать — статическое или динамически расширяемое, делать общий случай с темплейтами или конкретный, какие названия давать и т.п.). в общем, пока оставалась надежда выкрутиться малой кровью, но кажется, она на исходе
Re[2]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:16
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Здравствуйте, _hum_, Вы писали:


__>>p.s. T4r4sB, это же вы, по-моему, недавно говорили, что raw-ptr устарели


TB>Ну если ты пишешь контейнер, в котором сам управляешь памятью, то таки придётся с ними повозиться.

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

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

и вы что признаете, что raw-ptr все-таки не всегда можно заменить смартами?
Re[3]: Реализация дерева средствами с++11
От: T4r4sB Россия  
Дата: 16.04.16 13:21
Оценка:
Здравствуйте, _hum_, Вы писали:

__>и вы что признаете, что raw-ptr все-таки не всегда можно заменить смартами?


Да, не везде.
Но это не значит, что можно руками фигачить new/delete
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: Реализация дерева средствами с++11
От: -MyXa- Россия  
Дата: 16.04.16 13:22
Оценка:
Здравствуйте, _hum_, Вы писали:

__>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


Что конкретно за логика владения нарушается? Вроде, нормально работает.
Если не поможет, будем действовать током... 600 Вольт (C)
Re[2]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:36
Оценка:
Здравствуйте, -MyXa-, Вы писали:

MX>Здравствуйте, _hum_, Вы писали:


__>>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


MX>Что конкретно за логика владения нарушается? Вроде, нормально работает.


кто владелец-то? что делать с операторами присваивания и констркуторами копирования объектов, содержащих в себе в качестве данных такие деревья, и почему?
Re[4]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:39
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Здравствуйте, _hum_, Вы писали:


__>>и вы что признаете, что raw-ptr все-таки не всегда можно заменить смартами?


TB>Да, не везде.

TB>Но это не значит, что можно руками фигачить new/delete

кстати, оказывается, std::experimental::observer_ptr
Re[3]: Реализация дерева средствами с++11
От: -MyXa- Россия  
Дата: 16.04.16 13:46
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Здравствуйте, -MyXa-, Вы писали:


MX>>Здравствуйте, _hum_, Вы писали:


__>>>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


MX>>Что конкретно за логика владения нарушается? Вроде, нормально работает.


__>кто владелец-то?


Владелец чего именно?

__>что делать с операторами присваивания и констркуторами копирования объектов, содержащих в себе в качестве данных такие деревья, и почему?


А что бы ты делал при использовании голых указателей?
Если не поможет, будем действовать током... 600 Вольт (C)
Re[5]: Реализация дерева средствами с++11
От: -MyXa- Россия  
Дата: 16.04.16 13:50
Оценка:
Здравствуйте, _hum_, Вы писали:

__>кстати, оказывается, std::experimental::observer_ptr


На сколько мне известно, он ничего не делает. Это просто удобный способ отметить где у тебя, скорее всего, ошибка в программе.
Если не поможет, будем действовать током... 600 Вольт (C)
Re[4]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:56
Оценка:
Здравствуйте, -MyXa-, Вы писали:

MX>Здравствуйте, _hum_, Вы писали:


__>>Здравствуйте, -MyXa-, Вы писали:


MX>>>Здравствуйте, _hum_, Вы писали:


__>>>>При попытке перевести все на язык смартов возникают трудности: если делать на unique_ptr (что достаточно логично), то непонятно, как быть с указателем на родителя. Если же делать на shared_ptr / weak_ptr, то нарушается логика владения. Как в таком случае быть?


MX>>>Что конкретно за логика владения нарушается? Вроде, нормально работает.


__>>кто владелец-то?


MX>Владелец чего именно?


владелец узлов дерева

__>>что делать с операторами присваивания и констркуторами копирования объектов, содержащих в себе в качестве данных такие деревья, и почему?


MX>А что бы ты делал при использовании голых указателей?


ясно что — делал бы клонирование (в противном бы случае удаление одного объекта привело бы к некорректной работе другого). в случае же с shared клонирование необязательно — два объекта могу работать через шареды с одним и теми же узлами.
Re[6]: Реализация дерева средствами с++11
От: _hum_ Беларусь  
Дата: 16.04.16 13:59
Оценка:
Здравствуйте, -MyXa-, Вы писали:

MX>Здравствуйте, _hum_, Вы писали:


__>>кстати, оказывается, std::experimental::observer_ptr


MX>На сколько мне известно, он ничего не делает.



да, так и написано:

It is intended as a near drop-in replacement for raw pointer types, with the advantage that, as a vocabulary type, it indicates its intended use without need for detailed analysis by code readers.


MX> Это просто удобный способ отметить где у тебя, скорее всего, ошибка в программе.


?
Отредактировано 16.04.2016 14:01 _hum_ . Предыдущая версия .
Re[5]: Реализация дерева средствами с++11
От: -MyXa- Россия  
Дата: 16.04.16 14:08
Оценка:
Здравствуйте, _hum_, Вы писали:

__>ясно что — делал бы клонирование (в противном бы случае удаление одного объекта привело бы к некорректной работе другого). в случае же с shared клонирование необязательно — два объекта могу работать через шареды с одним и теми же узлами.


Так это-ж больше от задачи зависит. Надо клонировать — клонируй, не надо — не клонируй.
Если не поможет, будем действовать током... 600 Вольт (C)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.