Практическое применения метапрограммирования
От: SpiritFire  
Дата: 07.12.11 14:46
Оценка: 3 (1)
Недавно начал изучение "Современного проектирования на С++" Александреску. Соответственно, смотрел Loki, немного boost::mpl. Местами сложно, местами непривычно, но очень интересно.

Возникло несколько вопросов:

1. С чего начать изучение boost::mpl с надеждой на практическое применение?
Открыв бустовскую документацию по mpl вырвалось что-то вроде "<censored> и за что же браться?". Очень много всего, но 90% того, что я увидел, с моей точки зрения (могу ошибаться по неопытности) неприменимо в коммерческом проекте — либо слишком сложно для написания, понимания и сопровождения. Например, вывод всех символов 16-ричной системы счисления в константную строку во время компиляции я сделаю за 10 минут вручную (static const std::wstring A::digits="..."), или за 120 минут на mpl. Когда я уйду в отпуск, если это придётся подправить другому программисту — "хардкод" он подправит за 10 минут, а boost::mpl за 60. Если единственный аргумент в этом примере за mpl — это "идеологическая правильность", то выбор инструмента для души и для продакшена очевиден. Для души — mpl дома на коленке, поигрался и забыл, для продакшена — хардкод и беремся за следующую фичу.

Из того, что попалось на глаза, полезным показался if_ и if_c, sequence (для оптимизации) и семпл из .../libs/mpl/examples/integer.cpp — битовая маска заданного размера.
// в битовой маске как-то так:
typedef integer<32>::type mask32;
typedef integer<100>::type mask100;
static_assert( boost::is_same<mask32, uint32_t>);


Само собой, boost/std::function, bind, lambda я как mpl не рассматриваю. Это вещи для меня очень приятные и полезные.

2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
Поиск по форуму выдал огромную кучу информации, но ознакомившись с ней, я так и не понял, кто получил от использования mpl или Loki радость и облегчение, а кто — гордость за то, что "мы сами не верим, что смогли это сделать, не дай бог еще раз такой дизайн...".


Если кто-то поделится опытом не только "как надо", а и как "не надо" — буду тоже очень признателен, тема для меня актуальна и очень интересна.
Особенно интересует опыт использования в небиблиотечном коде. То есть, в обычном проекте, в котором немного повторно использованного кода, написанного рукастым или головастым девелопером, немного говнокода (куда ж без него? Работает и ладно, не досуг, заказчик просит новые фичи), заказчики сами не девелоперы и красоту настоящего С++ им не понять
Re: Практическое применения метапрограммирования
От: opener  
Дата: 07.12.11 14:50
Оценка:
Здравствуйте, SpiritFire, Вы писали:

SF>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?


Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.
Re: Практическое применения метапрограммирования
От: SpiritFire  
Дата: 07.12.11 15:09
Оценка:
Что использовал в проекте сам:

  • Каплю Policy-based desigh: Шаблонная структура с парой политик CopyAble и NonCopyable, выбор алгоритма через boost::if_ (точнее, через свой велоif);
  • Traits и похожие обёртки, например, LockedReference — обертка над ссылкой, "склеенная" с мьютексом;
  • Немного удаления квалификаторов: Убирание const-ов, volatile, *, &;

    На данный момент это всё, что увидел, пронравилось, потом реализовал и снова понравилось (причем не только мне).

    Особый вопрос насчет tumple... Не понимаю я эту идею. Насколько я понял, смысл такой же, как в std::pair<>, только с бОльшим числом членов. Может, я что-то не понимаю, но последний у меня аллергия:
    std::pair<std::string, std::string> user = collector->getUser(name);
    cout << user.first << ": " << user.second << std::endl;    // WTF?!! А если бы было еще и ->third и ->fourth
    
    TUser user = collector->getUser(name);
    cout << user.fullName << ": " << user.email << std::endl;  // всё понятно
  • Re[2]: Практическое применения метапрограммирования
    От: SpiritFire  
    Дата: 07.12.11 15:22
    Оценка:
    Здравствуйте, opener, Вы писали:
    SF>>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
    O>Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.

    Если не секрет, чем стандартные не понравились? Понадобилась межпоточная блокировка внутри, что-то еще, или просто boost не хотелось тянуть в проект?
    Re: Практическое применения метапрограммирования
    От: jazzer Россия Skype: enerjazzer
    Дата: 07.12.11 15:34
    Оценка: 11 (2) +1
    Здравствуйте, SpiritFire, Вы писали:

    SF>Если кто-то поделится опытом не только "как надо", а и как "не надо" — буду тоже очень признателен, тема для меня актуальна и очень интересна.

    Надо хорошо, не надо плохо Ну и меру надо знать.
    В общем, каких-то специальных советов тут нету.

    SF>Особенно интересует опыт использования в небиблиотечном коде. То есть, в обычном проекте, в котором немного повторно использованного кода, написанного рукастым или головастым девелопером, немного говнокода (куда ж без него? Работает и ладно, не досуг, заказчик просит новые фичи), заказчики сами не девелоперы и красоту настоящего С++ им не понять


    Вот небиблиотечный боевой: http://rsdn.ru/forum/cpp/2899831.1.aspx
    Автор: jazzer
    Дата: 02.04.08

    Вот полубиблиотечный: http://rsdn.ru/forum/cpp/3722136.1.aspx
    Автор: jazzer
    Дата: 02.03.10


    это то, что я публиковал, а так в рабочем коде еще мульон примеров.
    Например, использование switch_ от Стивена Ватанабе для свича по типам для диспатча сообщений согласно их типу, boost::fusion и boost::variant прямо в интерфейсе элементы MPL содержат, плюс у меня есть мегадевайс с блэкджеком и сериализацией, в котором намешано и шаблонного, и препроцессорного метапрограммирования (последнее — тоже вполне себе метапрограммирование, не одними шаблонами живы).

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


    А, вот, специальный совет для шаблонного програмирования (не обязательно мета-).
    Надо инстанцировать как можно меньше шаблонов и ловить ошибки как можно раньше, причем отлавливать их так, чтобы после них ничего не компилировалось, потому что иначе компилятор попытается покомпилировать то, что уже заведомо неверно и нагенерит мегабайтный листинг с ошибками.
    Т.е. вместо
    template<class T> void f(T x) {
      BOOST_MPL_ASSERT(boost::is_intergral<T>);
      // код, который использует Т и нагенерит кучу ненужных ошибок
      // в дополнение к сообщению об ошибке от ассерта выше
    }

    надо писать так:
    template<class T> void f_impl(T x, boost::false_type) {
      BOOST_MPL_ASSERT_MSG(false,BAD_TYPE,(T));
      // больше никакого кода!
      // зато можно (и нужно) поставить комментарий, как пофиксить
    }
    template<class T> void f_impl(T x, boost::true_type) {
      // код, который использует правильный Т и никаких ошибок не нагенерит
    }
    
    template<class T> void f(T x) {
      f_impl( x, boost::is_intergral<T>() );
    }


    Аналогичный прием используется и в самом бусте (примет из Спирита):
    static void define(rule&  lhs, Expr const&  expr, mpl::false_)
    {
       // Report invalid expression error as early as possible.
       // If you got an error_invalid_expression error message here,
       // then the expression (expr) is not a valid spirit qi expression.
       BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
    }
    jazzer (Skype: enerjazzer) Ночная тема для RSDN
    Автор: jazzer
    Дата: 26.11.09

    You will always get what you always got
      If you always do  what you always did
    Re: Практическое применения метапрограммирования
    От: night beast СССР  
    Дата: 07.12.11 15:46
    Оценка: 1 (1)
    Здравствуйте, SpiritFire, Вы писали:

    SF>Недавно начал изучение "Современного проектирования на С++" Александреску. Соответственно, смотрел Loki, немного boost::mpl. Местами сложно, местами непривычно, но очень интересно.


    SF>Возникло несколько вопросов:


    SF>1. С чего начать изучение boost::mpl с надеждой на практическое применение?


    хорошая книга:
    Aleksey Gurtovoy "C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond"
    Re[2]: Практическое применения метапрограммирования
    От: XuMuK Россия  
    Дата: 07.12.11 17:08
    Оценка:
    Здравствуйте, SpiritFire, Вы писали:

    SF>Что использовал в проекте сам:


    SF>Особый вопрос насчет tumple... Не понимаю я эту идею. Насколько я понял, смысл такой же, как в std::pair<>, только с бОльшим числом членов. Может, я что-то не понимаю, но последний у меня аллергия:

    по кортежам можно итерироваться иногда это очень удобно (например можно написать функцию для сериализации и все сериализуемые данные держать в кортежах)
    а чтобы не было WTF можно писать так:
    typedef boost::tuple<std::string, std::string> UserData;
    enum { NAME, EMAIL };
    UserData user = collector->getUser(name);
    cout << user.get<NAME>() << ": " << user.get<EMAIL>() << std::endl;
    Re: Практическое применения метапрограммирования
    От: IROV..  
    Дата: 07.12.11 17:23
    Оценка:
    Здравствуйте, SpiritFire, Вы писали:

    SF>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?

    Когда писал вот эту библиотеку https://sourceforge.net/projects/pybind/
    я не волшебник, я только учусь!
    Re: Практическое применения метапрограммирования
    От: Ytz https://github.com/mtrempoltsev
    Дата: 07.12.11 18:48
    Оценка:
    Для меня метапрограммирование — это в основном всякие проверки времени компиляции. Например так я проверял допустимость использования типа:


    typedef boost::mpl::vector<A, B, C> AllowedTypes;
    
    template<typename T>
    struct CheckAllowedType
    {
        typedef typename boost::mpl::contains<AllowedTypes, T>::type type;
    };
    
    #define IF_THIS_DOES_NOT_COMPILE_IT_MEANS_THAT_THE_TYPE_IS_NOT_SUPPORTED(T) \
        BOOST_MPL_ASSERT((CheckAllowedType<T>))
    
    template <typename T>
    void DoSomething(const T& value)
    {
        IF_THIS_DOES_NOT_COMPILE_IT_MEANS_THAT_THE_TYPE_IS_NOT_SUPPORTED(T);
        ...
    }
    Re: Практическое применения метапрограммирования
    От: Ytz https://github.com/mtrempoltsev
    Дата: 07.12.11 18:59
    Оценка:
    Еще вспомнил, недавно задача стояла вызывать одну функцию для классов производных от некоторого и другую для всех остальных:


    class Archive
    {
    public:
        template <typename T>
        Archive& operator>>(T& data)
        {
            return ReadImpl(typename boost::is_base_of<Data::Packable, T>::type(), data);
        }
    
    private:
        template <typename T>
        Archive& ReadImpl(boost::false_type, T& data)
        {
            ...
        }
    
        Archive& ReadImpl(boost::true_type, Data::Packable& data);
    };
    Re[3]: Практическое применения метапрограммирования
    От: opener  
    Дата: 07.12.11 20:20
    Оценка:
    Здравствуйте, SpiritFire, Вы писали:

    SF>Здравствуйте, opener, Вы писали:

    SF>>>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
    O>>Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.

    SF>Если не секрет, чем стандартные не понравились? Понадобилась межпоточная блокировка внутри, что-то еще, или просто boost не хотелось тянуть в проект?


    Проще было написать свой, чем разбираться, как прикрутить стандартный.
    Re[2]: Практическое применения метапрограммирования
    От: cppnick  
    Дата: 08.12.11 18:24
    Оценка:
    Здравствуйте, jazzer, Вы писали:

    J> // код, который использует Т и нагенерит кучу ненужных ошибок

    J> // в дополнение к сообщению об ошибке от ассерта выше
    А если static_assert, то он брякнет компилятор сразу же или будет дальше компилировать с генерацией "кучи ненужных ошибок"?
    Re[3]: Практическое применения метапрограммирования
    От: jazzer Россия Skype: enerjazzer
    Дата: 08.12.11 22:39
    Оценка:
    Здравствуйте, cppnick, Вы писали:

    C>Здравствуйте, jazzer, Вы писали:


    J>> // код, который использует Т и нагенерит кучу ненужных ошибок

    J>> // в дополнение к сообщению об ошибке от ассерта выше
    C>А если static_assert, то он брякнет компилятор сразу же или будет дальше компилировать с генерацией "кучи ненужных ошибок"?

    Обычно компиляторы стараются выдать за раз максимум ошибок, чтоб ты их сразу мог поправить, а не останвливаются после первой же.
    Так что даже со статик-ассертом они пытаются компилировать дальше насколько возможно и генерят сообщения об ошибках.
    jazzer (Skype: enerjazzer) Ночная тема для RSDN
    Автор: jazzer
    Дата: 26.11.09

    You will always get what you always got
      If you always do  what you always did
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.