На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Делегаты в сишном стиле и лямбды c++11 , "pure c" с элементами "c++11"
    Здравствуйте)

    Собственно, сам вопрос: подскажите пожалуйста, как мне обьявить делегат в сишном стиле так, чтобы я мог пихать на его место плюсовые лямбды, которые умеют работать с внешними переменными?

    Условие: нельзя использовать STL, в том числе, нельзя использовать functional

    Поясню подробнее:

    Вот пример того что я хочу сделать:
    ExpandedWrap disabled
      typedef void(*Delegate)();
       
      void test(Delegate action)
      {
        action();
      }
       
      int main()
      {
        int a = 0;
       
        auto func = []() // В этом место могло быть [=] или [*]
        {
          a++;
        };
       
        test(func);
      }


    Однако, очевидно что в таком виде переменная a недоступна лямбде, а в случае если я меняю тело [], получается что лямбда перестаёт соответствовать объявлению делегата Delegate.

    Подумываю попробовать сделать Delegate через templates, чтобы можно было сделать хотябы так:

    ExpandedWrap disabled
      Delegate<int> func = [](&a)
      {
        a++;
      }


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

    Прошу не ругаться по поводу черезжопности подхода к реализации :blush:
      Обертку сделай, по другому никак судя по всему.

      Добавлено
      Как то так: https://ideone.com/CYsIjc
      ExpandedWrap disabled
            #include <iostream>
            
            using namespace std;
            
            typedef void(*Delegate)();
            
            void test(Delegate action)
            {
              action();
            }
            
            int main() {
              int a = 0;
            
              auto func = [&a]() // В этом место могло быть [=] или [*]
              {
                ++a;
                std::cout << "func: " << a ;
              };
              static auto staticFn = func;
            
              Delegate ptr = []( ) { return staticFn(); };
            
              test(ptr);
              return 0;
            }


      Добавлено
      Ну и к слову, я уже вроде задавал такой вопрос, можешь еще тут посмотреть варианты решения: Cконвертировать замыкание со списком захвата в указатель на функцию С++14
        Цитата Wound @
        Cконвертировать замыкание со списком захвата в указатель на функцию С++14

        Интересно ещё то, что автор хочет сделать то же самое что мне и нужно :D

        Цитата Wound @
        Как то так: https://ideone.com/CYsIjc

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

        Ещё поковыряю
          Цитата VisualProg @
          Ещё поковыряю
          Как наковыряете что-нибудь - не сочтите за труд, выложите здесь. Тоже может понадобиться, запишу на корочку.
            Цитата VisualProg @
            Интересно ещё то, что автор хочет сделать то же самое что мне и нужно

            В принципе автор там сделал так, как ему ответили в последнем посту. Вроде работало.
              Ну, первое что пришло в голову, сделать прямую обёртку, по стопам Wound:

              ExpandedWrap disabled
                #define MakeLambdaAction(name, x) \
                    auto _tmp##name = x; \
                    static auto _static##name = _tmp##name; \
                    auto name = []() { _static##name(); }


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

              ExpandedWrap disabled
                    typedef void(*Delegate)();
                 
                    void test(Delegate action)
                    {
                        action();
                    }
                 
                 
                    int main()
                    {
                        int counter = 0;
                 
                        MakeLambdaAction(ptr, [&counter]()
                        {
                            std::cout << "timer tick...\n";
                            counter++;
                        });
                 
                        test(ptr);
                 
                    }


              1. Я надеюсь что этот код никто не увидит
              2. Я искренне надеюсь что мне не придётся использовать его в таком виде, хотя, очевидно, что это 100% решает мою проблему... :crazy:
              Сообщение отредактировано: VisualProg -
                Без захвата локальные переменные никак не обработать. Ссылки на них по-любому придётся как-то хранить, и шаблоны тут тоже не помогут, т.к. такие ссылки не могут быть разрешены в компайл-тайм. Единственный возможный вариант – зафиксировать ссылку на неё в ран-тайм. А возможно это только посредством её инициализации в статик сторадж после определения локальной переменной.
                Сообщение отредактировано: Qraizer -
                  Цитата VisualProg @
                  очевидно, что это 100% решает мою проблему... :crazy:

                  А в чем заключается исходная проблема?

                  Если вы захватываете что-то в лямбду, то вы теряете совместимость с указателем на функцию. И это естественно.

                  В Си классически используется другой подход. В изначальный callback добавляют void*-аргумент и позволяют устанавливать и callback и указатель на произвольные данные, который и будет передан в callback при вызове.

                  Что нужно-то?
                    Цитата D_KEY @
                    А в чем заключается исходная проблема?


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

                    В классическом случае, я вижу такое решение:

                    1. Под конкретную операцию завожу переменную хранящую время последнего запуска (например, в миллисекундах)
                    2. Каждую итерацию бесконечного цикла смотрю сколько времени прошло с момента предыдущего выполнения операции
                    3. Если операция не выполнялась достаточно долго (наш интервал и больше), ставлю её на выполнение.

                    Вот пример реализации одной такой операции в картинках и схемах в коде:
                    ExpandedWrap disabled
                      auto lastTimestamp = Time::getTimestamp();
                       
                      for(;;)
                      {
                        
                       auto timestamp = Time::getTimestamp();
                       
                       if(timestamp - lastTimestamp >= INTERVAL)
                       {
                        doSomething();
                        lastTimestamp = timestamp;
                       }
                       
                      }


                    Теперь добавим вторую операцию в этот цикл:

                    ExpandedWrap disabled
                      auto lastTimestampOperation1 = 0;
                      auto lastTimestampOperation2 = 0;
                       
                      for(;;)
                      {
                        
                       auto timestamp = Time::getTimestamp();
                       
                       if(timestamp - lastTimestampOperation1 >= INTERVAL1)
                       {
                        doSomething1();
                        lastTimestampOperation1 = timestamp;
                       }
                       
                       if(timestamp - lastTimestampOperation2 >= INTERVAL2)
                       {
                        doSomething2();
                        lastTimestampOperation2 = timestamp;
                       }
                       
                      }


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

                    Вот как у меня теперь наращиваются операции:

                    ExpandedWrap disabled
                      Time::Timer operation1(INTERVAL1, doSomething1());
                      Time::Timer operation2(INTERVAL2, doSomething2());
                       
                      for(;;)
                      {
                        
                       operation1.update();
                       operation2.update();
                       
                      }


                    Ну, или совсем красиво:

                    ExpandedWrap disabled
                      List<Time::TimedObject> operations(
                      {
                       Time::Timer(INTERVAL1, doSomething1()), // Добавление операций
                       Time::Timer(INTERVAL2, doSomething2()), // ...
                      });
                       
                      for(;;)
                      {
                       
                       operations.update();
                       
                      }


                    Добавлено
                    Так вот, я столкнулся с тем, что не всегда удобно формировать свой объект таймера с указателем на реальный метод, начал копать лямбды, и упёрся в вышеописанное...
                    Сообщение отредактировано: VisualProg -
                      Язык: c++
                      Стандарт: c++11
                      Железка: Arduino (целевая - Nano)
                      Из библиотек только сама голая ардуинная приблуда (там чистый c98)

                      Так что, говорим о c++11 без STL. Всё остальное допиливается руками, либо частично берутся готовые фрагменты реализаций из STL (если это возможно)
                      Не знаю чем это вам поможет...
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0430 ]   [ 16 queries used ]   [ Generated: 20.04.24, 01:00 GMT ]