У меня бывало так: смотришь в чужой или в свой собственный, но старый и основательно забытый, код и на уровне интуиции понимаешь -- сильно сложно, могло было быть и должно было быть проще. А вот "математически выразить" это не можешь. Как правило, пути и способы упрощения кода находились, но не сходу, а после тщательного обдумывания (что заставляет вспомнить замечательную мудрость "простота не предшествует сложности, а следует за ней"). Тем не менее, сам этот момент, когда чувствуешь, что что-то не так, а что именно понять не можешь, имеет место быть.
Вот как сегодня, когда я решил посмотреть пост Tiny Metaprogramming Library в блоге Eric Niebler. Триггером послужил приведенный ниже код функции tuple_cat, которая принимает на вход набор туплов, а возвращает новый тупл, в котором последовательно перечислены значения из всех туплов-аргументов:
namespace detail { template<typename Ret, typename...Is, typename ...Ks, typename Tuples> Ret tuple_cat_(typelist<Is...>, typelist<Ks...>, Tuples tpls) { return Ret{std::get<Ks::value>( std::get<Is::value>(tpls))...}; } } template<typename...Tuples, typename Res = typelist_apply_t< meta_quote<std::tuple>, typelist_cat_t<typelist<as_typelist_t<Tuples>...> > > > Res tuple_cat(Tuples &&... tpls) { static constexpr std::size_t N = sizeof...(Tuples); // E.g. [0,0,0,2,2,2,3,3] using inner = typelist_cat_t< typelist_transform_t< typelist<as_typelist_t<Tuples>...>, typelist_transform_t< as_typelist_t<make_index_sequence<N> >, meta_quote<meta_always> >, meta_quote<typelist_transform_t> > >; // E.g. [0,1,2,0,1,2,0,1] using outer = typelist_cat_t< typelist_transform_t< typelist<as_typelist_t<Tuples>...>, meta_compose< meta_quote<as_typelist_t>, meta_quote_i<std::size_t, make_index_sequence>, meta_quote<typelist_size_t> > > >; return detail::tuple_cat_<Res>( inner{}, outer{}, std::forward_as_tuple(std::forward<Tuples>(tpls)...)); } |
Пусть не обижаются читающие меня хорошие C++ники, которые считают, что это вполне нормально и есть случаи, когда такой код нужен, используется и приводит к хорошим результатам. Но как по мне, так это лютый звиздец. Так не должно быть.
Даже не прошу понять меня правильно. Жизнь слишком коротка, чтобы тратить изрядную ее часть на изобретение способов преодоления проблем, для решения которых язык не был предназначен, да еще и посредством негодных инструментов. Или на то, чтобы разбираться в таких вот решениях. И если подобный навороченный код становится нормой в мире C++, то возникает непреодолимое желание держатся от C++ в частности, да и от программирования вообще, как можно дальше.
Это типичный случай, когда интуитивно понимаешь, что слишком мудрено, что должно быть проще. А вот формализировать это впечатление не можешь, для этого нужно слишком много усилий. Разве что вспоминаются мои старые споры на RSDN-е с C++никами, которые считали, что Boost.Lambda, реализующая лямбда функции на шаблонах C++03, -- это нормально. И что пока встроенных язык лямбда-функций нет, вполне уместно пользоваться Boost.Lambda-ами. Если кто-то не понимает, о чем речь, то вот маленький пример из штатной документации к Boost.Lambda:
std::for_each(v.begin(), v.end(), ( switch_statement( _1, case_statement<0>(std::cout << constant("zero")), case_statement<1>(std::cout << constant("one")), default_statement(cout << constant("other: ") << _1) ), cout << constant("\n") ) ); |
Повторюсь, я не хочу никого убеждать в том, что это неправильно. Это моя личная точка зрения, которая никому не навязывается и вряд ли является единственно верной. Но, если подобные навороты становятся нормой, то где-то что-то не так. Возможно, во мне. Может я уже просто слишком стар для разработки ПО в современных условиях.