Исключения во время выполнения при использовании std :: functions. Почему они не указывают на действительный код? - PullRequest
3 голосов
/ 08 мая 2019

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

Моя библиотека основана на arduino-fsm , библиотеке, которая достигает этого путем определения Fsm,Государство и переходные объекты.arduino-fsm использует необработанные указатели на функции для установки функций, которые будут вызывать состояния и переходы (функции, которые охватывают, что делать, когда: вход в состояние, в состояние, выход из состояния, выполнение определенного перехода состояния).Эта библиотека позволяет очень интуитивное определение конечного автомата.Однако использование необработанных указателей на функции означает, что Fsm не может быть полезной переменной экземпляра объекта (необработанные указатели на функции не могут указывать на нестатические функции-члены, а статические функции-члены не могут получить доступ к переменным экземпляра - созданиеневозможно, чтобы несколько разных родительских объектов одного и того же класса выполняли разные экземпляры одного и того же Fsm.

Моя реализация заменяет использование необработанных указателей на функции с std::function s для определения функций States и Transitions вчтобы разрешить это (следующие ответы на этот вопрос ).

Я, кажется, падаю при первом препятствии, однако. У меня есть исключения во время выполнения, из-за которых мои обратные трассировки стека происходят из-замои std::function не указывают на действительный код ( ссылка ). Хотя я не могу понять, почему это происходит.

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


Библиотека определяет структуруFunctionState и класс FunctionFsm (FunctionFsm также определяет struct Transition).

FunctionState содержит три std::function s с именами on_enter, on_state и on_exit.Функции, на которые они указывают, вызываются всякий раз, когда FunctionState впервые вводится, активен и остается, соответственно.

FunctionFsm имеет функции для настройки переходов fsm, запуска конечного автомата и запуска переходов между состояниями.Он отслеживает текущее состояние и хранит список переходов, которые определяют конечный автомат.

Переходы содержат указатели на состояние, из которого они перемещаются, и состояние, в которое они перемещаются, и целое число «событие» (которое допускает определенные переходытриггером) и std :: function, которая указывает на функцию, вызываемую в этом переходе (после вызова функции выхода из первого состояния).

В целом std::function s используются, чтобы сообщать каждомууказать, что это за три функции, и указать переходам, какую функцию он должен использовать.(Если никакие действия не требуются для какой-либо конкретной функции состояния или перехода, std::function может быть установлен на nullptr - и библиотека будет обрабатывать его соответствующим образом.)


Мой простой пример не работаетв настройке fsm, когда он определяет первый переход для конечного автомата.

Важные биты из FunctionFsm в библиотеке:

struct Transition {
  FunctionState* state_from;
  FunctionState* state_to;
  int event;
  std::function<void()> on_transition;
};

Transition create_transition(FunctionState* state_from,
                             FunctionState* state_to,
                             int event,
                             std::function<void()> on_transition){
  Transition t;
  t.state_from = state_from;
  t.state_to = state_to;
  t.event = event;
  t.on_transition = on_transition;

  return t;
}

void add_transition(FunctionState* state_from,
                    FunctionState* state_to,
                    int event,
                    std::function<void()> on_transition){
  if(state_from == NULL || state_to == NULL) return;

  Transition transition = FunctionFsm::create_transition(state_from,
                                                         state_to,
                                                         event,
                                                         on_transition);
  //stuff to keep track of number of transitions and add transition to the list
  //m_transitions is just a Transition*, manual memory management copied
  //like-for-like from arduino-fsm (which doesn't use stdlib features)
  m_transitions = (Transition*) realloc (m_transitions,
                                         (m_num_transitions + 1)
                                          * sizeof(Transition));
  m_transitions[m_num_transitions] = transition;
  m_num_transitions++;
}

Пример:

char a = 'a';
char b = 'b';

//state functions
void a_on_enter(){ Serial.print("Entering a: "); }
void a_on(){ Serial.print(a); }
void a_on_exit(){ Serial.println(" - exitting a. "); }
void a_on_trans_b(){ Serial.println("Moving from a to b."); }

void b_on_enter(){ Serial.print("Entering b: "); }
void b_on(){ Serial.print(b); }
void b_on_exit(){ Serial.println(" - exitting b. "); }
void b_on_trans_a(){ Serial.println("Moving from b to a."); }

//states
FunctionState state_a(&a_on_enter, &a_on, &a_on_exit);
FunctionState state_b(&b_on_enter, &b_on, &b_on_exit);

//fsm    
FunctionFsm fsm(&state_a);  //state_a is initial state

//...

//add transitions
fsm.add_transition(&state_a, &state_b, TOGGLE_SWITCH, &a_on_trans_b);//crashes here
fsm.add_transition(&state_b, &state_a, TOGGLE_SWITCH, &b_on_trans_a);

//... code to run fsm, and trigger transitions as appropriate (hasn't had a chance to run yet)

Я отмечаю, что сбой происходит при первой попытке присвоить std :: function значение, которое показывает, что я где-то неправильно обращаюсь в библиотечных функциях ... Но я действительно не могу этого понять.Я не думаю, что сама функция была вызвана где-либо, просто ее назначил std :: function?

Сама ошибка, безусловно, this (как мне говорит мой микроконтроллересть) - я просто не могу понять, почему ...

Я задавался вопросом, слишком ли я просил слишком много неявного преобразования, предоставляя функции, которые вызывали std::function<void()> с необработанными указателями на функции, но мое тестирование показывает,без улучшения, если я явно создаю std :: functions, а затем передаю их вместо этого.

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


Если у кого-то, имеющего больше опыта использования std::function, есть какие-либо полезные предложения, я был бы очень благодарен.Я застрял и не знаю, как двигаться дальше.

1 Ответ

0 голосов
/ 08 мая 2019

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

Оказывается, проблема была в использованииУказатель переходов и realloc для хранения списка переходов fsm очень на языке C, а не на std::vector<Transition> (намного больше C ++).Первоначально я отмечал это как плохую практику, но не учел возможный эффект.

Я так и не понял полные детали того, почему использование realloc испортило мои std::function s, но я, конечно, могу видеть неясность того, откуда возникла эта проблема.

Обновив библиотеку, чтобы вместо нее использовать std::vector, все работает отлично.

Если кто-то захочетдать реальное объяснение проблемы с использованием realloc, в этом случае мне было бы очень интересно услышать это!(Я бы наверняка отметил это как ответ!)

...