Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается?
Кстати, автор этого кода, P. J. Plauger, в соавторстве со всем известным Керниганом написал книжку по хорошему стилю оформления кода — The Elements of Programming Style, "...advocating the notion that computer programs should be written not only to satisfy the compiler or personal programming "style", but also for "readability" by humans, specifically software maintenance engineers, programmers and technical writers."
Вообще, ничего там сложного, просто стиль форматирования довольно редкий и имена начинаются с подчеркивания и большой буквы, как и положено для стандартной библиотеки.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
ну да, понимаем. и буст тоже. а что там сложного-то? в стандартной библиотеке обычный код, только все имена с __ или _X
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
Понимаю, причём понимаю также, откуда там ноги растут.
Это библиотека, идущая вместе с компилятором, и отвечающая двум требованиям:
— чтобы компилировалась как можно быстрее (отсюда — короткие имена)
— чтобы не смешивалась с пользовательскими библиотеками (отсюда — имена в зарезервированном для компилятора формате: _Caps и __doubleunderscore)
Очень может быть, что она исходно написана по-человечески, а потом на неё натравили обфускатор.
Ну а алгоритмическая часть там довольно прозрачная.
Единственно, что некоторые места унаследованы с древних времён, и там, где можно было бы обойтись новомодными штучками вроде decltype, используются многоэтажные диспетчеризации.
Но у меня двадцать лет опыта, в том числе, регулярного заглядывания за кулисы системных библиотек. Могу только посочувствовать тем, кто туда впервые сунулся.
Здравствуйте, Кодт, Вы писали:
К>Очень может быть, что она исходно написана по-человечески, а потом на неё натравили обфускатор.
Степанов в одном из выступлений (где именно не помню, если вспомню то скину) рассказывал что некий тип взял его код, пропустил через pretty-printer, а потом продал Microsoft за много $.
Здравствуйте, Juster, Вы писали: J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
Да там просто все, что в СТЛ, что в бусте, причем в бусте или и имена вменяемые... Главное — повыкидывать из поля зрения всякие не относящиеся к тебе ifdef-ы — они нужны, только чтоб поддерживать ту кучу платформ и компиляторов, которые они поддерживают, включая древние версии GCC и MSVC (и борланда, ага). Сейчас понемножку убирают поддержку старья, но то, что уже написано, пока что там лежит.
В общем, можешь либо через препроцессор пропустить, либо редактор настроить, чтоб определенные ifdef/endif сворачивал или другим цветом показывал, чтоб они в глаза не лезли — после этого все будет просто.
Например, файл common_type.hpp для C++11 выдает (g++ --std=c++11 -E -fdirectives-only):
Скрытый текст
namespace boost {
// prototypetemplate<typename... T>
struct common_type;
// 1 argtemplate<typename T>
struct common_type<T>
{
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(T) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (T));
public:
typedef T type;
};
// 2 argsnamespace type_traits_detail {
template <class T, class U>
struct common_type_2
{
private:
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(T) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (T));
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(U) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (U));
static bool declval_bool(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<T>::type declval_T(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<U>::type declval_U(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<bool>::type declval_b();
public:
typedef decltype(declval<bool>() ? declval<T>() : declval<U>()) type;
};
template <class T>
struct common_type_2<T, T>
{
typedef T type;
};
}
template <class T, class U>
struct common_type<T, U>
: public type_traits_detail::common_type_2<T,U>
{ };
// 3 or more argstemplate<typename T, typename U, typename... V>
struct common_type<T, U, V...> {
public:
typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
};
} // namespace boost
а для С++03 (g++ --std=c++03 -E -fdirectives-only):
Скрытый текст
namespace boost {
// prototypetemplate <class T, class U = void, class V = void>
struct common_type
{
public:
typedef typename common_type<typename common_type<T, U>::type, V>::type type;
};
// 1 argtemplate<typename T>
struct common_type<T, void, void>
{
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(T) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (T));
public:
typedef T type;
};
// 2 argsnamespace type_traits_detail {
template <class T, class U>
struct common_type_2
{
private:
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(T) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (T));
BOOST_COMMON_TYPE_STATIC_ASSERT(sizeof(U) > 0, BOOST_COMMON_TYPE_MUST_BE_A_COMPLE_TYPE, (U));
static bool declval_bool(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<T>::type declval_T(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<U>::type declval_U(); // workaround gcc bug; not required by stdstatic typename add_rvalue_reference<bool>::type declval_b();
public:
typedef BOOST_TYPEOF_TPL(declval_b() ? declval_T() : declval_U()) type;
};
template <class T>
struct common_type_2<T, T>
{
typedef T type;
};
}
template <class T, class U>
struct common_type<T, U, void>
: public type_traits_detail::common_type_2<T,U>
{ };
// 3 or more args
} // namespace boost
Сразу видна разница в реализации для С++11 с вариадиками и для обычного С++03 с дефолтными void.
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
Понять-то можно, но вот то, что писатели (как правило) совершенно не думают о читателях или удобном использовании — это почти всегда так.
Вот вчера пытался переносимо округлить float до int...
И тут началось... Не, ну я понимаю, задача сложная, непростая... Ну, это примерно как конвертировать int в строку: тысячи идиотских примеров привязанных то к архитектуре, то к системе. Каждый год новое слово в конвертостроении.
Но я, как дисциплинированный инженер, открываю boost, нахожу boost::math::iround, читаю. Ага, может бросить исключение... Так, исключение мне не нужно. Мне даже сообщение об ошибке не нужно. Мне нужно значения выходящие за пределы пределы представления int конвертировать в минимальное и в максимальное значения. Открываю код:
template <class T, class Policy>
inline int iround(const T& v, const Policy& pol)
{
BOOST_MATH_STD_USING
T r = boost::math::round(v, pol);
if((r > (std::numeric_limits<int>::max)()) || (r < (std::numeric_limits<int>::min)()))
return static_cast<int>(policies::raise_rounding_error("boost::math::iround<%1%>(%1%)", 0, v, 0, pol));
return static_cast<int>(r);
}
template <class T>
inline int iround(const T& v)
{
return iround(v, policies::policy<>());
}
вижу policy. Думаю, ну теперь-то надо просто подкрутить немного policy, подменив policies::policy<>() на что-то своё. Открываю рекомендации от авторов: о майн год! Писатели рекомендуют переопределять функции обработки ошибок прямо в пространстве имён boost::math::policies ! Это меня несколько настораживает, но не останавливает. Я довожу до конца изменения кода, компилирую и получаю с какими-то варнингами то, что мне нужно. Задаю запредельное значение и прохожу в дебагере, внезапно вижу рядом код:
template <class T, class TargetType>
inline T raise_rounding_error(
const char* ,
const char* ,
const T& val,
const TargetType&,
const ::boost::math::policies::rounding_error< ::boost::math::policies::errno_on_error>&)
{
errno = ERANGE;
// This may or may not do the right thing, but the user asked for the error
// to be silent so here we go anyway:return std::numeric_limits<T>::is_specialized ? (val > 0 ? (std::numeric_limits<T>::max)() : -(std::numeric_limits<T>::max)()): val;
}
Понимаю, что я сделал что-то лишнее. Ищу способ использовать эту функцию. Натыкаюсь на багфикс: здесь. Странно, у меня версия 1.53, а выглядит так же. Но "добило" меня это:
Caution
There is a danger of One-Definition-Rule violations if you add ad-hoc macros to more than one source files: these must be set the same in every translation unit.
И тут я понял, что с таким подходом нарушение ODR рано или поздно случится. Проект большой, за всеми и всем не уследишь. Я уже однажды ловил нарушение ODR — три недели поисков. Спасибо, больше не надо.
Пришлось всё откатить назад, а код написать "ручками".
Вот так я и не смог округлить float до int...
А вы говорите "ногти". Если бы только это!
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается? Я уж про буст не говорю, там пока разберешься во всех typedef, ifdef, все ногти перекусаешь
Писателям бы показать код реализации таких же контейнеров в Java. Или это не лечится?
BFE>Понять-то можно, но вот то, что писатели (как правило) совершенно не думают о читателях или удобном использовании — это почти всегда так.
Чукча не читатель. А нечукча поймёт любой код, в том числе и дизассемблерный листинг.
На кывте вообще собрались нежинки какие-то: код для них должен быть обязательно без напряга
читаем, и это чуть ли не главный критерий его качества. Вы, товарищи, разрабы или нежные барышни?
ЗЫ хватит опенсорсный код называть говнокодом.
Здравствуйте, Кодт, Вы писали:
К>Это библиотека, идущая вместе с компилятором, и отвечающая двум требованиям: К>- чтобы компилировалась как можно быстрее (отсюда — короткие имена)
???
K>— чтобы не смешивалась с пользовательскими библиотеками (отсюда — имена в зарезервированном для компилятора формате: _Caps и __doubleunderscore)
А для чего namespace тогда?
Здравствуйте, smeeld, Вы писали:
S>На кывте вообще собрались нежинки какие-то: код для них должен быть обязательно без напряга S>читаем, и это чуть ли не главный критерий его качества. Вы, товарищи, разрабы или нежные барышни?
оффтоп: это как на хабре любую статью, в т.ч. техническую, оценивают по критерию "легко читается" :hz:
Здравствуйте, smeeld, Вы писали:
BFE>>Понять-то можно, но вот то, что писатели (как правило) совершенно не думают о читателях или удобном использовании — это почти всегда так. S>Чукча не читатель. А нечукча поймёт любой код, в том числе и дизассемблерный листинг. S>На кывте вообще собрались нежинки какие-то: код для них должен быть обязательно без напряга S>читаем, и это чуть ли не главный критерий его качества. Вы, товарищи, разрабы или нежные барышни?
Я, товарищ, считаю так: если время на чтение кода превышает время его написания, то и читать не стоит.
В том же boost'е есть и хороший код, например circular_buffer. Всё понятно, всё работает, вплоть до обращений с отрицательным индексом к итераторам ( it[-2] ).
S>ЗЫ хватит опенсорсный код называть говнокодом.
Дык другого в общем доступе нет.
Здравствуйте, flаt, Вы писали:
K>>— чтобы не смешивалась с пользовательскими библиотеками (отсюда — имена в зарезервированном для компилятора формате: _Caps и __doubleunderscore) F>А для чего namespace тогда?
Здравствуйте, flаt, Вы писали:
F>Здравствуйте, Кодт, Вы писали:
К>>Это библиотека, идущая вместе с компилятором, и отвечающая двум требованиям: К>>- чтобы компилировалась как можно быстрее (отсюда — короткие имена) F>???
Ну зависимость скорости от длины идентификаторов всё же есть. Это видно на всяких синтетических тестах, где в файле миллион функций с длинами имён в 10 либо в 100 символов. Более длинный файл банально дольше читать :)
Впрочем, большая часть времени компиляции всё равно тратится не на лексический разбор, так что зависимость от длины идентификаторов не слишком сильная — их же всего-навсего нужно один раз прочитать, сложить в какую-нибудь хеш-таблицу, а дальше оперировать только ссылками, где уже длина не играет такой значительно роли. Собственно, если запустить какой-нибудь компилятор в режиме препроцессинга (например, clang++ -E) и в режиме компилирования (clang++ -c), то абсолютное замедление при использовании более длинных имён будет сравнимо, что как бы свидетельствует, что именно на сам процесс компиляции длина сказывается совсем слабо.
В общем, я бы не рассматривал способ сокращения длины идентификаторов как способ ускорения компиляции пока их длина находится в разумных пределах, а не составляет килобайт на каждое имя.
Да и есть более практичные способы ускорения, так использование precompiled headers для стандартной библиотеки даст куда больше выгоды.
K>>— чтобы не смешивалась с пользовательскими библиотеками (отсюда — имена в зарезервированном для компилятора формате: _Caps и __doubleunderscore) F>А для чего namespace тогда?
Есть места, где namespace использовать нельзя — внутри классов, например. Если реализации std::map нужен идентификатор для типа или данных, то завернуть его в namespace не получится, а дать ему «нормальное» имя тоже — вдруг это имя совпадёт случайно с пользовательским, если он вдруг, например, отнаследуется от класса (это ведь не запрещено, хотя и не рекомендуется для стандартных контейнеров).
W>Есть места, где namespace использовать нельзя — внутри классов, например. Если реализации std::map нужен идентификатор для типа или данных, то завернуть его в namespace не получится, а дать ему «нормальное» имя тоже — вдруг это имя совпадёт случайно с пользовательским, если он вдруг, например, отнаследуется от класса (это ведь не запрещено, хотя и не рекомендуется для стандартных контейнеров).
Можно подробнее?
Вот банальный код
template<class _Tp>
class vector
{
public:
typedef _Tp value_type; // что не так может быть с T, что его меняют на _Tp?void push_back(const value_type& __x); // что не так может быть с x, что его нужно заменять на __x?
};
Здравствуйте, flаt, Вы писали:
F>Здравствуйте, watchmaker, Вы писали:
W>>Есть места, где namespace использовать нельзя — внутри классов, например. Если реализации std::map нужен идентификатор для типа или данных, то завернуть его в namespace не получится, а дать ему «нормальное» имя тоже — вдруг это имя совпадёт случайно с пользовательским, если он вдруг, например, отнаследуется от класса (это ведь не запрещено, хотя и не рекомендуется для стандартных контейнеров). F>Можно подробнее?
Да пожалуй я тут слишком преувеличил проблему. Хотя при совпадении имён может компилироваться код, который компилироваться не должен — то есть если stl использует имена с __, а программа не использует, то допустить опечатку, приводящую к ложному совпадению будет сложнее :)
F>Про макросы понятно, но если говорить о С++?
А в C++ макросы никуда не делись. Ну то есть в вышеприведённых двух случаях они вполне являются достаточной причиной не использовать имена T и x.
Здравствуйте, watchmaker, Вы писали:
W>Да пожалуй я тут слишком преувеличил проблему. Хотя при совпадении имён может компилироваться код, который компилироваться не должен — то есть если stl использует имена с __, а программа не использует, то допустить опечатку, приводящую к ложному совпадению будет сложнее
Не буду сейчас лазить в потроха реального STL, просто покажу на примере, где может аукнуться
namespace std
{
namespace aux
{
template<class It> void insertion_sort(It i, It j) { ..... }
template<class It> void quick_sort (It i, It j) { ..... }
}
// интерфейсная функцияtemplate<class It>
void sort(It i, It j)
{
if(distance(i,j) < some_treshold)
aux::insertion_sort(i,j);
else
aux::quick_sort(i,j);
}
}
Если кто-то кое-где определил для своих нужд
namespace aux // откуда мы знали, что это имя задействовано в STL?
{
struct bar {...};
void quick_sort(bar* x, bar* y) // "упорядочим два элемента по-быстрому" - откуда мы знали, что это имя значит что-то иное в std::aux ?
{
if(x->key > y->key) { quick_swap(x->key, y->key); quick_swap(x->value, y->value); }
}
}
чуть выше точки включения #include <algorithm>
то у нас посыплется сортировка std::vector<aux::bar> в релизной сборке.
Здравствуйте, Juster, Вы писали:
J>Я когда туда заглядываю, мне сразу закрыть хочется. Такое ощущение, что код был создан авто-генератором каким-то, форматирование неудобное, имена жутко запутанные, структура довольно сложная. Кто-то в этом разбирается?
К>namespace aux // откуда мы знали, что это имя задействовано в STL?
К>{
К> struct bar {...};
К> void quick_sort(bar* x, bar* y) // "упорядочим два элемента по-быстрому" - откуда мы знали, что это имя значит что-то иное в std::aux ?
К> {
К> if(x->key > y->key) { quick_swap(x->key, y->key); quick_swap(x->value, y->value); }
К> }
К>}
К>
К>Не буду сейчас лазить в потроха реального STL, просто покажу на примере, где может аукнуться К>
К>namespace std
К>{
К> namespace aux
К> {
К> template<class It> void insertion_sort(It i, It j) { ..... }
К> template<class It> void quick_sort (It i, It j) { ..... }
К> }
К> // интерфейсная функция
К> template<class It>
К> void sort(It i, It j)
К> {
К> if(distance(i,j) < some_treshold)
К> aux::insertion_sort(i,j);
К> else
К> aux::quick_sort(i,j);
К> }
К>}
К>
К>Если кто-то кое-где определил для своих нужд К>чуть выше точки включения #include <algorithm> К>то у нас посыплется сортировка std::vector<aux::bar> в релизной сборке.
Да с чего бы ей сыпаться? Да и от сборки как это может зависить?
namespace aux // откуда мы знали, что это имя задействовано в STL?
{
struct bar {...};
void quick_sort(bar* x, bar* y) // "упорядочим два элемента по-быстрому" - откуда мы знали, что это имя значит что-то иное в std::aux ?
{
if(x->key > y->key) { quick_swap(x->key, y->key); quick_swap(x->value, y->value); }
}
}
namespace std
{
namespace aux
{
template<class It> void quick_sort (It i, It j) { ..... }
}
// интерфейсная функцияtemplate<class It>
void sort(It i, It j)
{
aux::quick_sort(i,j); // это однозначно std::aux::quick_sort, так как aux продикларирована до и находится внутри
}
}
Здравствуйте, B0FEE664, Вы писали:
BFE>Да с чего бы ей сыпаться? Да и от сборки как это может зависить? BFE>А вот если сказать:
Безо всяких using-хаков
#include <cstdio>
namespace aux {
struct foo {};
void quick_sort(foo* f1, foo* f2) { printf("user-defined function\n"); }
}
namespace sss {
namespace aux {
template<class T> void quick_sort(T i, T j) { printf("standard quick sort\n"); }
template<class T> void sort_impl(T i, T j) { quick_sort(i,j); } // мы же, якобы, внутри родного неймспейса, - не квалифицируем имя
}
template<class T> void sort(T i, T j) { aux::sort_impl(i,j); } // здесь имя квалифицированное, компилятор ищет в нужном пространстве
}
int main() {
aux::foo p,q;
sss::sort(&p, &q); // "user-defined function"
}
Здравствуйте, Кодт, Вы писали:
BFE>>Да с чего бы ей сыпаться? Да и от сборки как это может зависить? BFE>>А вот если сказать: К>Безо всяких using-хаков
Переставим sss и aux местами:
#include <cstdio>
namespace sss {
namespace aux {
template<class T> void quick_sort(T i, T j) { printf("standard quick sort\n"); }
template<class T> void sort_impl(T i, T j) { quick_sort(i,j); } // мы же, якобы, внутри родного неймспейса, - не квалифицируем имя
}
template<class T> void sort(T i, T j) { aux::sort_impl(i,j); } // здесь имя квалифицированное, компилятор ищет в нужном пространстве
}
namespace aux {
struct foo {};
void quick_sort(foo* f1, foo* f2) { printf("user-defined function\n"); }
}
int main() {
aux::foo p,q;
sss::sort(&p, &q); // "user-defined function"
}
Ничего не изменилось. Но поведение интересное, согласен.
Здравствуйте, B0FEE664, Вы писали:
BFE>Ничего не изменилось. Но поведение интересное, согласен.
Тут надо уточнить. ADL — дело тонкое... Я точно не помню, какие там правила попадания в поле зрения.
Когда поле зрения ограничено — от начала файла до точки использования, а когда — от начала до конца файла.