На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (3) 1 [2] 3  все  ( Перейти к последнему сообщению )  
> Аналог std::function , Реализация класса типа std::function, с movable-параметрами
    Цитата Serpentus @
    Это можно только гадать. Может просто не додумались. Или не посчитали нужным (или даже вредным, Qraizer объяснил почему).

    Всегда должны быть причины. Очень сильно сомневаюсь, что они там до чего-то не додумались.

    Цитата Serpentus @
    . А еще у вызова через std::function нулевой оверхед, а у вызова через std::unique_ptr<CFunction> - нет (пусть и не большой).

    Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же.
      В общем, остановился вот на таком варианте
      ExpandedWrap disabled
        template <typename> struct CFunctionInvoker;
        template <size_t... Indexes>
        struct CFunctionInvoker<std::index_sequence<Indexes...>>
        {
            template <typename TFunc, typename TArgs, typename... TT>
            static auto Invoke(TFunc &&func, TArgs &&args, TT&&... args2)
            {
                return std::invoke(func, std::get<Indexes>(args)..., std::forward<TT>(args2)...);
            }
         
            template <typename TFunc, typename TArgs, typename... TT>
            static auto InvokeOnce(TFunc func, TArgs args, TT&&... args2)
            {
                return std::invoke(func, std::move(std::get<Indexes>(args))..., std::forward<TT>(args2)...);
            }
         
            template <typename TFunc, typename... TT>
            static auto Invoke(TFunc &&func, std::tuple<>, TT&&... args2)
            {
                return std::invoke(func, std::forward<TT>(args2)...);
            }
         
            template <typename TFunc, typename TArgs, typename... TT>
            static auto InvokeOnce(TFunc func, std::tuple<>, TT&&... args2)
            {
                return std::invoke(func, std::forward<TT>(args2)...);
            }
         
        };
         
        template <typename TRes, typename... TArgs>
        class CFunction
        {
        public:
            typedef std::unique_ptr<CFunction> TPtr;
         
            template <typename TFunc, typename... TT>
            static TPtr Make(TFunc &&func, TT&&... args)
            {
                return std::make_unique<CFunctionImpl<CFunction, TFunc, TT...>>(std::forward<TFunc>(func), std::forward<TT>(args)...);
            }
         
            virtual ~CFunction() {;}
         
            template <typename... TT>
            TRes Call(TT&&... args)
            {
                return _Call(std::forward<TT>(args)...);
            }
         
            template <typename... TT>
            TRes operator()(TT&&... args)
            {
                return Call(std::forward<TT>(args)...);
            }
         
        protected:
            virtual TRes _Call(TArgs... args) = 0;
         
            template <typename TFunction, typename TFunc, typename... TT>
            class CFunctionImpl
            : public TFunction
            {
            public:
                CFunctionImpl(TFunc &&fn, TT&&... args)
                : m_fn(std::forward<TFunc>(fn))
                , m_args(std::forward<TT>(args)...)
                {
                }
         
                virtual TRes _Call(TArgs... args) override
                {
                    return CFunctionInvoker<std::index_sequence_for<TT...>>::Invoke(m_fn, m_args, args...);
                }
            protected:
                TFunc m_fn;
                std::tuple<std::decay_t<TT>...> m_args;
            };
         
        };


      Избавился от std::placeholders. На большее меня не хватает.
        Цитата Serpentus @
        ...Это можно только гадать. Может просто не додумались. :) Или не посчитали нужным (или даже вредным, Qraizer объяснил почему).
        Я уверен, что правильно указал причину. Во всех официальных вариантах реализаций, что в крайне скудном функционале C++98, что в более мощном варианте, появившемся в бусте, функциональные объекты полностью отвечали семантике значений. Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными. Если б в C++11 решили, что это не нужно, программисты б сильно удивились.
          Цитата Qraizer @
          Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными.

          Там не в копировании проблема, а отсутствии move
            Так я как раз об этом. Копирование и перемещение – это разные семантики. Я бы не хотел вдруг узнать что после a=b источник разрушен. Конечно, по дефолту так и не будет, ибо нужно хотя бы a=std::move(b) написать. Но дело не в этом. Из-за документированной семантики копирования куча библиотек уже использовали функциональные объекты в части своей реализации. И как именно, одним только их разработчикам ведомо, и то не факт. Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте.

            Добавлено
            В общем, если это свой собственный велосипед, это ещё куда ни шло, хотя бы видно, что не std. Но в std такую фичу вводить вряд ли будут, опасно это.
              Цитата Qraizer @
              Так я как раз об этом. Копирование и перемещение – это разные семантики.

              Никто с этим не спорит. Проблема в том, что там ты ничего не копируешь.
              Где ты видишь копирование в std::function<....> fn = std::bind(.....)?
              Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем?

              Цитата Qraizer @
              Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте.

              Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них?
                Цитата Олег М @
                Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же.

                Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму.
                  Цитата Serpentus @
                  Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму.

                  А ты std::function инициализировал через std::bind с параметрами?
                    Цитата Олег М @
                    Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем?
                    :facepalm: Проехали.
                    Цитата Олег М @
                    Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них?
                    Контейнеры крайне редко использовались программистами в роли обычных переменных обычным для программиста способом, т.к. все понимали, что их копирование суть тяжёлая операция и всячески старались это оптимизировать, а уж для библиотечного кода и подавно.
                      Цитата Qraizer @
                      Проехали.

                      И что же мы проехали?

                      Насколько мне известно, std::function появилась в 11-м стандарте, где move-семантика уже вовсю использовалась. О каком старом коде и о каких привычках программистов может идти речь?
                      Объясни мне, почему вот этот код компилируется
                      ExpandedWrap disabled
                        std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!"));
                        auto fn = [sp = std::move(sp)]()
                        {
                            std::cout << *sp << std::endl;
                        };
                         
                        auto fn2 = std::move(fn);
                        fn2();


                      А вот этот - нет
                      ExpandedWrap disabled
                        std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!"));
                        std::function<void()> fn = [sp = std::move(sp)]()
                        {
                            std::cout << *sp << std::endl;
                        };


                      Хотя принципиального отличия лично я не вижу.


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

                      Т.е. копирование обычной переменной это считается нормальным? Или я как-то не понимаю, что значит "обычная переменная"?
                        Цитата Олег М @
                        Объясни мне, почему вот этот код компилируется
                        А вот этот - нет
                        Хотя принципиального отличия лично я не вижу.

                        Потому что в первом случае - некий сгенерированный компилятором класс, который (в данном случае) будет Moveable, но не CopyConstructable (согласно требованиям стандарта), а во втором - std::function, со строго специфицированным внешним поведением. Ну не может у тебя std::function быть в одном случае копируемым, а в другом - перемещаемым (в зависимости от того, чем его инициализировали). А вот лямбда-класс - может, потому что безымянный с минимально специфицированным публичным интерфейсом.
                          Да уж. Дошло наконец-то.
                          Т.е. действительно, здесь без виртуального метода не обойтись.
                            :D Фуф. :D
                              Цитата Олег М @
                              А ты std::function инициализировал через std::bind с параметрами?

                              И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()").
                              Сообщение отредактировано: Serpentus -
                                Цитата Serpentus @
                                И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()").

                                Я думаю, если инициализация std::function и использование будут в разных единицах трансляции - то код будет немного другим, с косвенным вызовом. Но я могу ошибаться.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) 1 [2] 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0438 ]   [ 17 queries used ]   [ Generated: 19.04.24, 04:52 GMT ]