На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Автоматизация для C++-программистов
    Пока Цайне Куль, он же Никонов, он же Пархоменко рассыпается в многошуме на тему супер-пупер-языков и всего такого прочего, поговорим здесь про более практичные вещи.
    Если кто не в курсе, в этом апреле я ездил на конференцию ACCU с докладом, посвящённым тому, как с использованием clang'а можно написать утилиту, которая превращает исходный код в исходный код. (Как только организаторы конференции выложат доклады в открытый доступ - дам на него ссылку. Ну, или идите в мой about в клубе - там есть). Но доклад, это хорошо, а пример реализации такой утилиты - ещё лучше. И такой пример есть у меня! :) Лежит здесь: flex_lib/codegen

    Что это, собственно, такое. Это утилита, которая собирается вместе с инфраструктурай кланга (то есть включает в себя clang compiler frontend). Ей передаётся на вход файл с исходным текстом и тип того, что нужно сгенерировать. Она пропускает файл через компилятор, получает AST, и на базе этого AST генерирует новый код, содержащий реализацию требуемого функционала. В настоящий момент утилита умеет:

    - Генерировать функции конвертации enum'ов в строки и обратно.
    - Генерировать стандартную реализацию прокси-методов в соответствии с pimpl-идиомой.

    Планируется всё это дорабатывать и расширять, в частности:
    - Добавить генерацию boost-сериализаторов для структур и классов.
    - Добавить ORM-мэппинг для, скажем, SQLite'а.
    - Генерация mock-классов для Google Mock.
    - Чего-нибудь ещё полезного.

    Да-да. В C++ до сих пор нет рефлекшена и метаклассов. Приходится извращаться. :)
    Сообщение отредактировано: Flex Ferrum -
      Генерация pimpl-вызовов допилена и выложена в репозиторий. Собственно, о чём это. Предполагаю, что с идиомой pimpl знакомы если не все, то многие. Основная сложность в её использовании - это необходимость в буквальном смысле шаблонной копипасте реализаций методов публичного класса. Шаблонность заключается в том, что вся реализация этих методов - это делегация вызова соответствующему методу приватного класса. То есть, положим, мы имеем такую декларацию публичного класса:
      ExpandedWrap disabled
        class TestPimplImpl;
         
        class TestPimpl : flex_lib::pimpl<TestPimplImpl>
        {
        public:
            explicit TestPimpl(uint32_t number = 0);
            explicit TestPimpl(std::string number);
            explicit TestPimpl(TestMoveable&& obj) noexcept;
            ~TestPimpl() noexcept;
            
            const std::string GetString() const;
            const uint32_t GetNumber() const;
            void SetGeneratedValues(unsigned values[10]);
            const std::array<unsigned, 10> GetGeneratedValues() const;
            void ResetValues(int num, std::string str);
            PimplMode GetCurrentMode() const;
            const int* GetMoveablePtr() const noexcept;
            
            bool operator == (const TestPimpl& other) const;
        };

      Для каждого публичного метода в этой декларации (включая конструкторы) необходимо будет написать реализацию типа:
      ExpandedWrap disabled
        void TestPimpl::ResetValues(int num, std::string str)
        {
            m_impl->ResetValues(num, std::move(str));
        }

      Вот эти вот генерацией этих шаблонных реализаций методов и занимается генератор кода. Он проверяет характер сигнатуры функции, типы аргументов, где надо - применяет move-операции, и всё такое прочее. Есть желание к нему прикрутить так же чекер класса-реализации, чтобы он как минимум предупреждал, что сигнатуры методов поменялись, что-то добавилось. А как максимум - сам вносил необходимые "точечные" изменения.
        Flex Ferrum
        И как же разработчики виндоуса без всего этого обходились?
          Цитата Pavia @
          И как же разработчики виндоуса без всего этого обходились?

          Ой, не знаю. :D
            Цитата Flex Ferrum @
            Генерация pimpl-вызовов допилена и выложена в репозиторий.

            Флекс, раскидай, пожалуйста, на пальцах - в чем профит?

            Желательно просто по пунктам, типа "раньше вы делали вот так для такой задачи, и это было плохо, патамушта! .... а сейчас это здорово, и делается вот - так ... и это лучше патамушта...". Кстати, если не заленишься - можно будет пополнить раздел FAQ.
              Цитата JoeUser @
              Флекс, раскидай, пожалуйста, на пальцах - в чем профит?

              Так я же в 7-ом посте как раз "на пальцах" всё и раскидал.
                Цитата Flex Ferrum @
                Так я же в 7-ом посте как раз "на пальцах" всё и раскидал.

                Упс ... 7-й пост, это какой? По номеру - это тот, которым ты толькашта ответил. Дай правильный линк на него, плс.
                  Но могу ещё раз. Есть фасадный класс:
                  ExpandedWrap disabled
                    class TestPimplImpl;
                     
                    enum PimplMode
                    {
                        NormalMode,
                        AbnormalMode
                    };
                     
                    class TestMoveable
                    {
                    public:
                        TestMoveable(int value = 10)
                            : m_object(new int(value))
                        {
                        }
                        
                        const int* GetObject() const {return m_object.get();}
                        
                    private:
                        std::unique_ptr<int> m_object;
                    };
                     
                    class TestPimpl : flex_lib::pimpl<TestPimplImpl>
                    {
                    public:
                        explicit TestPimpl(uint32_t number = 0);
                        explicit TestPimpl(std::string number);
                        explicit TestPimpl(TestMoveable&& obj) noexcept;
                        ~TestPimpl() noexcept;
                        
                        const std::string GetString() const;
                        const uint32_t GetNumber() const;
                        void SetGeneratedValues(unsigned values[10]);
                        const std::array<unsigned, 10> GetGeneratedValues() const;
                        void ResetValues(int num, std::string str);
                        PimplMode GetCurrentMode() const;
                        const int* GetMoveablePtr() const noexcept;
                        
                        bool operator == (const TestPimpl& other) const;
                    };

                  Есть класс-реализация:
                  ExpandedWrap disabled
                    class TestPimpl;
                     
                    class TestPimplImpl
                    {
                    public:
                        TestPimplImpl(TestPimpl*, uint32_t number)
                            : m_intNumber(number)
                        {        
                        }
                     
                        TestPimplImpl(TestPimpl*, std::string number)
                            : m_strNumber(number)
                        {
                        }
                        
                        TestPimplImpl(TestPimpl*, TestMoveable&& moveable)
                            : m_moveableObj(std::move(moveable))
                        {
                        }
                        
                        const std::string GetString() const
                        {
                            return m_strNumber;
                        }
                        const uint32_t GetNumber() const
                        {
                            return m_intNumber;
                        }
                        void SetGeneratedValues(unsigned values[10])
                        {
                            std::copy(values, values + 10, begin(m_values));
                        }
                        const std::array<unsigned, 10> GetGeneratedValues() const
                        {
                            return m_values;
                        }
                        void ResetValues(int num, std::string str)
                        {
                            m_intNumber = num;
                            m_strNumber = str;
                        }
                     
                        PimplMode GetCurrentMode() const
                        {
                            return m_curMode;
                        }
                        const int* GetMoveablePtr() const
                        {
                            return m_moveableObj.GetObject();
                        }
                        
                        bool operator == (const TestPimplImpl& pimpl) const
                        {
                            return m_intNumber == pimpl.m_intNumber;
                        }
                        
                    private:
                        uint32_t m_intNumber;
                        std::string m_strNumber;
                        std::array<unsigned, 10> m_values;
                        PimplMode m_curMode = AbnormalMode;
                        TestMoveable m_moveableObj = TestMoveable(5);
                    };

                  Нужно реализовать методы фасадного класса так, чтобы они перевызывали методы класса-реализации. То есть ручками написать вот такой вот код:
                  ExpandedWrap disabled
                    TestPimpl::TestPimpl(uint32_t number)
                        : pimpl(this, number)
                    {
                    }
                     
                    TestPimpl::TestPimpl(std::string number)
                        : pimpl(this, std::move(number))
                    {
                    }
                     
                    TestPimpl::TestPimpl(TestMoveable &&obj) noexcept
                        : pimpl(this, std::move(obj))
                    {
                    }
                     
                    TestPimpl::~TestPimpl() noexcept = default;
                     
                    const std::string TestPimpl::GetString() const
                    {
                        return m_impl->GetString();
                    }
                     
                    const uint32_t TestPimpl::GetNumber() const
                    {
                        return m_impl->GetNumber();
                    }
                     
                    void TestPimpl::SetGeneratedValues(unsigned int values[10])
                    {
                        m_impl->SetGeneratedValues(values);
                    }
                     
                    const std::array<unsigned int, 10> TestPimpl::GetGeneratedValues() const
                    {
                        return m_impl->GetGeneratedValues();
                    }
                     
                    void TestPimpl::ResetValues(int num, std::string str)
                    {
                        m_impl->ResetValues(num, std::move(str));
                    }
                     
                    PimplMode TestPimpl::GetCurrentMode() const
                    {
                        return m_impl->GetCurrentMode();
                    }
                     
                    const int * TestPimpl::GetMoveablePtr() const noexcept
                    {
                        return m_impl->GetMoveablePtr();
                    }
                     
                    bool TestPimpl::operator==(const TestPimpl &other) const
                    {
                        return m_impl->operator==(*other.m_impl.get());
                    }

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

                  Добавлено
                  Цитата JoeUser @
                  Упс ... 7-й пост, это какой? По номеру - это тот, которым ты толькашта ответил. Дай правильный линк на него, плс.

                  Тьфу ты, второй, конечно же... Автоматизация для C++-программистов (сообщение #3722616)
                    Цитата Flex Ferrum @
                    И, после того, как ты эту пачку типовых реализаций напишешь один раз

                    Флекс, не не не :) Я пока не хочу разбираться во всей этой кухне пока не пройму профит. Зачем это все??? Что решается, что упрощается, что оптимизируется??? Мне это важно.

                    Вижу портянки кода. Это же должно чем-то оправдываться? Вот именно это пока интересует.
                      Цитата JoeUser @
                      Флекс, не не не Я пока не хочу разбираться во всей этой кухне пока не пройму профит. Зачем это все???

                      А, тебе не понятно, что есть такое pimpl? Это один из самых популярных способов скрыть реализацию. То есть описать стабильный публичный интерфейс, и отдельно - приватный внутренний. Причём приватный пользователям публичного интерфейса не виден (Private IMPLementation). Профит в том, что после этого ты можешь совершенно свободно менять детали реализации не затрагивая клиентов публичного интерфейса.
                        Цитата Flex Ferrum @
                        Профит в том, что после этого ты можешь совершенно свободно менять детали реализации не затрагивая клиентов публичного интерфейса.
                        Вообще-то это делает простая инкапсуляция с сокрытием кишок в непубличных элементах. PImpl имеет профит втрое круче:
                        1. все преимущества чёрного ящика, скрытого от публичного интерфейса;
                        2. непубличные элементы вообще не светятся в интерфейсе класса;
                        3. модификация непубличных сущностей не требует даже перекомпиляции клиентов, а с случае .dll или там .so, даже перелинковки.
                        На предмет последнего я слегка сутрировал, это достигается посредством PPImpl :D
                          Тут вот возник вопрос про мультиметоды и автогенерацию кода. А ведь это возможно! И решение будет проще, чем на шаблонах. И, главное, универсальнее, т. к. можно поддержать мультиметоды с арностью больше двух.
                            Flex Ferrum, ты мою тему помнишь? Там арность неограничена по сути. И кастомизируемое связывание для каждого параметра, хошь динамическое, хошь статическое.
                              Цитата Qraizer @
                              Flex Ferrum, ты мою тему помнишь? Там арность неограничена по сути. И кастомизируемое связывание для каждого параметра, хошь динамическое, хошь статическое.

                              Надо откопать.
                                В натуре, как-то глубоко закопалась, с третьей попытки только Поиском нашёл.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0430 ]   [ 17 queries used ]   [ Generated: 28.03.24, 23:57 GMT ]