Двусвязный список

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Скажу сразу я с С или С++ не знаком. Вот код, я тут пытался сделать менюшку через двусвязный список 

// пины энкодера
#define CLK 4 // s2
#define DT 3 // s1
#define SW 2 // key

#include "GyverEncoder.h"

Encoder enc1(CLK, DT, SW);  // для работы c кнопкой

class MenuItem {
  public:
    void (*handler)();
    String _name;
    MenuItem* sub;
    MenuItem* parent;
    MenuItem* back;
    MenuItem* next;

    MenuItem(String Name, void (*Handler)()) {
      handler = Handler;
      _name = Name;
      sub = NULL;
      parent = NULL;
      back = NULL;
      next = NULL;
    }

    void Invoke()  {
      (*handler)();
    }
};

class MenuManager {
  private:
    // MenuItem* first;


  public:
    MenuItem* current;
    MenuManager()  {
      current = NULL;
    }
    void Append(MenuItem* item)  {
      if (current != NULL) {
        Serial.println("1. "+ item->_name + " "+ current->_name);
        current->next = item;
      }

      item->back = current;
      current = item;
      Serial.println("3. "+ item->_name + " "+ current->_name);
    }
    void AddSub(MenuItem* item)  {
      if (current != NULL) {
        current->parent = item;
      }

      current->sub = item;
      current = item;
    };
    MenuItem* Next()  {
      if (current->next != NULL) {
        current = current->next;
      }

      return current;
    };
    MenuItem* Back()  {
      if (current->back != NULL) {
        current = current->back;
      }

      return current;
    };
};

MenuManager mm;

void setup() {
  Serial.begin(9600);
  enc1.setType(TYPE1);

  mm = MenuManager();
  mm.Append(&MenuItem("First", &test));
  Serial.println("2. "+mm.current->_name);
  mm.Append(&MenuItem("Second", &test2));
}

void loop() {
  enc1.tick();
  MenuItem* item = NULL;

//  if (enc1.isRight()) {
//    Serial.println("isRigh");
//    item = mm.Next();
//  }
//  if (enc1.isLeft()) {
//    Serial.println("isLeft");
//    item = mm.Back();
//  }
//
//  if (item != NULL) {
//    Serial.println(mm.current->_name);
//  }

  //
  //  if (item != ) {
  //
  //    //Serial.println(item->_name);
  //   // item->Invoke();
  //  } else {
  //    Serial.println("fdfdfdfdfffffffffffff");
  //  }
}


// Обработчики
void test() {
  Serial.println("test");
}

void test2() {
  Serial.println("test2");
}

вот результат из консоли 

3. First First
2. PSI R
1. Second Second
3. Second Second
 
 
 
какая-то чушь. 
Я ожидал увидеть вот такой результат
3. First First
2. First 
1. First First 
3. Second Second
 
как под пунктом 1 может в current быть Second если этот принт выводится до переприсваевания current и что за 2. PSI R такое? 
rkit
Offline
Зарегистрирован: 23.11.2016

Не усложняй себе жизнь. Массива тут хватит за глаза.

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

не хватит, я хочу ветвистое меню. Ну и потом тут уже интерес разыгрался 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Списки это для создания объектов с изначально не известным количеством в КУЧЕ.

Вы же делаете что то невнятное с очевидным результатом !

ArtemLaz пишет:

как под пунктом 1 может в current быть Second если этот принт выводится до переприсваевания current 

044 строка - условие проверьте ! Код выполняется всегда так как вы его написали !!!

ArtemLaz пишет:

Скажу сразу я с С или С++ не знаком

Не с того вы начали знакомство с С или С++ !!!

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

Было дело, мастырил заготовку меню, да так и не применил. Использовал библиотеку Qlist, чтобы не заморачиваться с динамическими массивами вручную, в расчете в промышленной версии переделать по уму. Публикую в расчете на конструктивную критику. 



#include "QList.h"

class Item {
  public:
    Item(Item* parent= nullptr) {_parent = parent;}
    const char* _name {"Main"};                     // name of current menu item
    Item* _parent = nullptr;                        // parents pointer. Nullptr for root
    uint8_t _retFrom {0};                           // number of child-item, from witch was "go to parent" executed. first (0) by default
    QList <Item*> child;                            // children refs list 
    size_t childCount {0};                          // children qty
    uint8_t _nrAtParentsList {0};                   // items number at parent's child-list
    Item* newChild(const char* nme, void (*ptr)()); // create a new child end-point with action
    void (*actionF)() {nullptr};                    // pointer to executable routine   
  private:
};


Item* Item::newChild(const char* nme, void (*ptrA)() = nullptr) {
  Item* p = new(Item);
  p->_name = nme;
  p->_parent = this;
  p->_nrAtParentsList = childCount++;
  p->actionF = ptrA;
  child.push_back(p);
  return p;
}

Item menu;

void actFunc(void){
  Serial.println("!! ACTION !!");
}

void setup() {
  Serial.begin(115200);
  delay(200);
  Serial.println("START");
  createMenu();
  printMenu(&menu);
}

void createMenu(void){                          // Creating demo-menu using different ways
  Item* file = menu.newChild("File");  
  Item* newFile= file->newChild("New");
  newFile->newChild("Option1");
  file->child.at(0)->newChild("Option2");
  file->child.at(0)->newChild("Option3");
  Item* edit = menu.newChild("Edit");
  edit->newChild("EditFirstSub");
  Item* undo = edit->newChild("Undo2");
  undo->newChild("Last");
  menu.child.at(1)->child.at(0)->newChild("All", actFunc);
  menu.child.at(1)->child.at(0)->newChild("Nothing");
  menu.child.at(1)->newChild("Redo");
  menu.child.at(1)->child.at(1)->newChild("Last");
  menu.child.at(1)->child.at(1)->newChild("All");
  menu.child.at(1)->child.at(1)->newChild("Nothing");
  menu.newChild("Info", actFunc);
}


void printMenu(Item* m){                          // Menu strtucture printout. Shows up to 3 levels 
  for (uint16_t i = 0; i< m->child.size(); i++){
    if (m->_parent) {
      if (m->_parent->_parent) {
        Serial.print(m->_parent->_parent-> _name); Serial.print("->"); Serial.print(m->_parent-> _name); Serial.print("->");
      }
      else {
        Serial.print(m->_parent-> _name);Serial.print("->");
      }
    }
    Serial.print(m->_name); Serial.print("->"); Serial.println(m->child.at(i) -> _name);
    if (m->child.at(i)->actionF != nullptr) m->child.at(i)->actionF();
    if (m->child.at(i) -> child.size()){
      printMenu(m->child.at(i));
    }
  }
}


void loop() {
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Без PROGMEM накладных расходов много.

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

нет у меня цели изучать С или С++, мне нужно скетч написать, я бы рад его не писать на С или С++. 

"Списки это для создания объектов с изначально не известным количеством в КУЧЕ."

Тут вы не правы 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

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

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

ArtemLaz пишет:

"Списки это для создания объектов с изначально не известным количеством в КУЧЕ."

Тут вы не правы 

А в чем он не прав? Что количество неизвестно? Или что в КУЧЕ?

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

sadman41 пишет:

Без PROGMEM накладных расходов много.

PROGMEM я планировал использовать, пока я хочу сделать навигацию по меню 

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

нет у меня цели изучать С или С++, мне нужно скетч написать, я бы рад его не писать на С или С++.

тогда вам в платный раздел - там пишут для всех. и для тех кто не знает С++ - тоже.

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

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

BOOM пишет:

ArtemLaz пишет:

"Списки это для создания объектов с изначально не известным количеством в КУЧЕ."

Тут вы не правы 

А в чем он не прав? Что количество неизвестно? Или что в КУЧЕ?

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

sadman41
Offline
Зарегистрирован: 19.10.2016

А с PROGMEM уже прожект MicroMenu имеется. Можно его взять за основу.

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

b707 пишет:

тогда вам в платный раздел - там пишут для всех. и для тех кто не знает С++ - тоже.

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

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

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

sadman41 пишет:

Без PROGMEM накладных расходов много.

Если это мне, то да, я это понимаю. В финальном варианте будет оптимизация. Правда с у четом того, что я использую ЕСП-шки нужно будет в каждом конкретном проекте смотреть, что нужно оптимизировать - ПЗУ или ОЗУ. 

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

 Вопрос я задал в первом сообщении.

Я не вижу там конкретного вопроса. Потому и ответы такие.

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Rumata пишет:

Было дело, мастырил заготовку меню, да так и не применил. Использовал библиотеку Qlist, чтобы не заморачиваться с динамическими массивами вручную, в расчете в промышленной версии переделать по уму. Публикую в расчете на конструктивную критику. 

спасибо за код. Попробую. 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

b707 пишет:

ArtemLaz пишет:

 Вопрос я задал в первом сообщении.

Я не вижу там конкретного вопроса. Потому и ответы такие.

 

конкретный вопрос таков: 

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

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

delete

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ArtemLaz ну так пройди свой код по шагам и поймешь почему 1 second

после выхода из setup - элементы MenuItem гарантированно существуют только в вашем больном воображении !!! Изучите время жизни переменных.

Смысла обсуждать полный бреда код не вижу !

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Komandir пишет:

ArtemLaz ну так пройди свой код по шагам и поймешь почему 1 second

после выхода из setup - элементы MenuItem гарантированно существуют только в вашем больном воображении !!!

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

 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Komandir пишет:

Смысла обсуждать полный бреда код не вижу !

не обсуждайте, тем более у вас не выходит 

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

Там есть глобальная переменная 

глобальная переменная - это указатель на класс. Она никуда не девается, да. А сам класс, созданный в setup() - после выхода из сетапа не существует.

Попытки написать что-либо сложнее блинк, не читая учебник по С++ - имхо, обречены на провал.

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

mm глобальная, а MenuItem ? Чему равны MenuItem после выхода из setup ? Вы уверены что эта область памяти по прежнему принадлежит MenuItem ?

В первом проходе Append - current равен NULL и сразу попадаем на 49 50 52 вывод 3 ...

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

delete

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Komandir пишет:

mm глобальная, а MenuItem ? Чему равны MenuItem после выхода из setup ? Вы уверены что эта область памяти по прежнему принадлежит MenuItem ?

В первом проходе current равен NULL и сразу попадаем на вывод 3 ...

mm глобальная переменная, я ожидал, что в ней будет сохранена ссылка (в current) на тот объект который я передал, или это не так работает? 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

смотрите, строка 84, добавил item, 85 строка уже выдает ерунду, почему? Я ожидал что там будет "First"

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

что бы объект гарантированно существовал его надо создать в КУЧЕ через new - тогда он будет существовать пока вы его сами не удалите через delete

с порядком вывода - так и не дошло что ли ? я на чукотском не умею доносить !

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ArtemLaz пишет:

смотрите, строка 84, добавил item, 85 строка уже выдает ерунду, почему? Я ожидал что там будет "First"

84 - пройдите по шагам переход в Append !!! (именно в Append ошибка)

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Komandir пишет:

что бы объект гарантированно существовал его надо создать в КУЧЕ через new - тогда он будет существовать пока вы его сами не удалите через delete

с порядком вывода - так и не дошло что ли ? я на чукотском не умею доносить !

мммм, вот уже дельные советы пошли 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

Komandir пишет:

ArtemLaz пишет:

смотрите, строка 84, добавил item, 85 строка уже выдает ерунду, почему? Я ожидал что там будет "First"

84 - пройдите по шагам переход в Append !!! (именно в Append ошибка)

не понимаю в чем ошибка, ну ткните носом, не вижу я где ошибка. в 50й строке item присваевается current в 51й строке корректно выводится корректно, а в 85 уже не корректно. 

ПС 

насчет "по шагам" ну в ардуинке нет отладчика 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ArtemLaz пишет:

смотрите, строка 84, добавил item, 85 строка уже выдает ерунду, почему? Я ожидал что там будет "First"

84 - пройдите по шагам переход в Append !!! (именно в Append ошибка)

вот в том то и дело, что вы думаете что MenuItem("First") существует - он создался для вызова Append и после выхода из Append - эта память уже используется для вызова следующей функции и что эта функция туда положила никому не известно - вот вы и видите ХЗ что в выводе - хорошо вообще serial.print не подвис ...

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

блиин с new все заработало, Komandir вот за это спасибо. С аппендом все ок 

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

все, можно закрывать тему, работает и навигация как я и ожидал. И да, ребят будьте добрее, ответ мог быть таков "ты не верно создал экземпляр класса, надо использовать new", а вы тут развели непонятно что. 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

А вот теперь расскажите нам зачем на Arduino динамическое меню ?

Вангую следующий холивар из-за нехватки памяти и/или пересечения кучи со стеком !!!

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

С аппендом все ок 

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

b707
Offline
Зарегистрирован: 26.05.2017

Komandir пишет:

Вангую следующий холивар из-за нехватки памяти и/или пересечения кучи со стеком !!!

однозначно. В классе с динамическими переменными обязательно должен быть явный деструктор. иначе будет утечка памяти. А так как это пишет ТС - с неявным созданием экземпляров в вызове Апппенд и тому подобное - утечка у него будет колоссальная

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

b707 явный деструктор - для того кому не нужно знание C и С++ это же как корове седло

rkit
Offline
Зарегистрирован: 23.11.2016

ArtemLaz пишет:

не хватит, я хочу ветвистое меню.

вообще никак не связано

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

rkit пишет:

Не усложняй себе жизнь. Массива тут хватит за глаза.

Можно и без массива. С ветвистостью которой каждый Алень позавидует.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

qwone пишет:

 С ветвистостью которой каждый Алень позавидует.

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

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

rkit пишет:

ArtemLaz пишет:

не хватит, я хочу ветвистое меню.

вообще никак не связано

тут можно подискутировать что из этого есть "усложнение жизни", связанные списки (односвязные/двусвязные) прекрасно ложатся на эту задачу. Вот как изящно создаются пункты меню 

mm->Append(new MenuItem("Settings: fan", &fan))->Append(new MenuItem("Settings: lamp", &lamp))->Append(new MenuItem("Settings: temp", &temp));

не думаю что с массивами так же было бы 

 

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

Вот как изящно создаются пункты меню 

mm->Append(new MenuItem("Settings: fan", &fan))->Append(new MenuItem("Settings: lamp", &lamp))->Append(new MenuItem("Settings: temp", &temp));

не думаю что с массивами так же было бы 

лучше покажите. как вы их так же изящно удаляете по окончании работы...

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

b707 пишет:

ArtemLaz пишет:

Вот как изящно создаются пункты меню 

mm->Append(new MenuItem("Settings: fan", &fan))->Append(new MenuItem("Settings: lamp", &lamp))->Append(new MenuItem("Settings: temp", &temp));

не думаю что с массивами так же было бы 

лучше покажите. как вы их так же изящно удаляете по окончании работы...

что удаляю? 

rkit
Offline
Зарегистрирован: 23.11.2016

Строка в 147 символов

"изящно"

уж не позорился бы

ArtemLaz
Offline
Зарегистрирован: 20.03.2022

rkit пишет:

Строка в 147 символов

"изящно"

уж не позорился бы

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

ArtemLaz пишет:
тут можно подискутировать что из этого есть "усложнение жизни", связанные списки (односвязные/двусвязные) прекрасно ложатся на эту задачу. Вот как изящно создаются пункты меню

Это изящно для больших машин, где ОЗУ дохера . А здесь ОЗУ маловато, и есть много флеш, которое не ОЗУ. 

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

пс:#298

b707
Offline
Зарегистрирован: 26.05.2017

ArtemLaz пишет:

b707 пишет:

ArtemLaz пишет:

Вот как изящно создаются пункты меню

лучше покажите. как вы их так же изящно удаляете по окончании работы...

что удаляю? 

пункты меню. Каждая команда new должна сопровождаться соответвующим delete, программа этого сама не делает. А так, как вы пишете - будут утечки на каждой строчке. Так даже на ПК с его гигами памяти непозволительно писать, что уж говорить о ардуине. где озу жалкие крохи.