Различные объявления qsort_r на Mac и Linux

Давайте посмотрим функцию qsort_r в Linux (/usr/include/stdlib.h):

typedef int (*__compar_d_fn_t)(const void *, const void *, void *);

extern void qsort_r (void *__base, size_t __nmemb, size_t __size,
         __compar_d_fn_t __compar, void *__arg)
  __nonnull ((1, 4));

Давайте посмотрим функцию qsort_r в Mac (/usr/include/stdlib.h):

void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *));

Как видите, эти объявления отличаются друг от друга (последовательность аргументов). Это удивительно! Будет ли эффективно где-то жаловаться, чтобы решить эту проблему?

2 ответа

Решение

Будет ли эффективно где-то жаловаться, чтобы решить эту проблему?

Увы нет. Так было слишком долго, и слишком много кода полагалось на него.

Я думаю, что основной вопрос заключается в том, " почему происходят эти несовместимости "? Я отвечу на это. Похоже, что сначала все сводится к BSD, но с плохим интерфейсом. ISO и позже GNU исправили интерфейс и решили, что нарушение совместимости того стоит. И Microsoft делает все, что им хочется.

Как указано @Downvoter (великое имя), qsort_r это нестандартная функция. Было бы неплохо, если бы это было стандартно, но на это нельзя полагаться. qsort_s является своего рода стандартом в Приложении K C11, но на самом деле никто не реализует C11, не говоря уже о его приложениях, и вопрос о том, является ли Приложение K хорошей идеей, находится под вопросом.

Как и многие проблемы с C и Unix, это связано с BSD против GNU против Microsoft и их неспособностью координировать расширения C. Linux это GNU. OS X - смесь многих вещей, но для C это следует BSD.

FreeBSD добавлен qsort_r в сентябре 2002 года. Visual Studio 2005 немного отличается qsort_s, ISO снова формализован qsort_s в 2007 году. Наконец, GNU появились спустя годы в glibc 2.8 в 2008 году, очевидно, после ISO. Вот старая ветка с 2004 по 2008 гг. qsort_r быть реализован в glibc, который имеет некоторые обоснования.

Напомню, вот qsort как определено в C99.

void qsort(
    void *base, size_t nmemb, size_t size,
    int (*compar)(const void *, const void *)
);

FreeBSD был первым в сентябре 2002 года. Они решили, что qsort_r должен сломать qsort интерфейс и поставить аргумент "thunk" перед функцией сравнения.

void qsort_r(
    void *base, size_t nmemb, size_t size,
    void *thunk,
    int (*compar)(void *, const void *, const void *)
);

Зачем? Вам нужно будет спросить Гарретта Уоллмана, который написал патч. Глядя на патч вы можете увидеть по его изменениям CMP было решено, что первым делом был "бандит". Возможно, они решили, что "функция сравнения идет в конце" - это то, что люди запомнят. К сожалению это значит qsort а также qsort_r Функции сравнения имеют свои аргументы в обратном порядке. Очень запутанно.


Между тем Microsoft, когда-либо новатор, имеет qsort_s в Visual Studio 2005.

void qsort_s(
   void *base, size_t num, size_t width,
   int (__cdecl *compare )(void *, const void *, const void *),
   void * context
);

"s" для "защищенного", а не "r" для "реентерабельного", который все остальные использовали, возможно, в соответствии с соглашением ISO (см. ниже) или наоборот. Они ставят "Thunk" в конце qsort_s сохраняя аргументы так же, как qsort, но для максимальной путаницы "thunk" идет в начале функции сравнения, как BSD. Они выбрали худший из возможных вариантов.


Что еще хуже, в 2007 году ISO опубликовал TR 24731-1, чтобы добавить проверку границ в стандартную библиотеку C (спасибо @JonathanLeffler за указание на это). И да, у них есть свои qsort_r, но это называется qsort_s! И да, это отличается от всех остальных!

errno_t qsort_s(
    void *base, rsize_t nmemb, rsize_t size,
    int (*compar)(const void *x, const void *y, void *context),
    void *context
);

Они мудро решили сохранить аргументы qsort_s и его функция сравнения надмножество qsort вероятно, утверждая, что людям будет легче запомнить. И они добавили возвращаемое значение, вероятно, хорошая идея. Чтобы добавить к путанице, в то время это был "Технический отчет", а не часть стандарта C. Теперь это "Приложение K" стандарта C11, все еще опционально, но имеет больший вес.


GNU решила то же самое, возможно, следуя ISO qsort_s,

void qsort_r(
    void *base, size_t nmemb, size_t size,
    int (*compar)(const void *, const void *, void *),
    void *arg
);

Глядя на добавление патча glibc qsort_r это было, вероятно, также легче реализовать. Чтобы знать наверняка, вам придется спросить Ульриха Дреппера.


Решение BSD об обмене аргументами с qsort и его функция сравнения, вероятно, вызывала много путаницы и ошибок на протяжении многих лет. Решение ISO / GNU оставить их прежними, возможно, лучше. ИСО решила дать ему другое имя. GNU решил нарушить совместимость с функцией BSD. Microsoft решила сделать что угодно. Теперь мы застряли с четырьмя несовместимыми реализациями. Поскольку функции сравнения имеют разные сигнатуры, макрос совместимости нетривиален.

(Это все реконструкция из кода. Для их реальных обоснований вам придется копаться в архивах списков рассылки.)

Я не могу винить GNU, BSD, ISO или Microsoft... хорошо, я могу винить Microsoft в том, что она намеренно пыталась убить C. Дело в том, что процесс стандартизации C, расширение этого стандарта и компиляторы следуют этому стандарту. мучительно медленно, и авторам компилятора иногда приходится делать то, что целесообразно.

Как написано здесь, qsort стандартизирован (C99), но qsort_r является расширением GNU ("qsort_r() был добавлен в glibc в версии 2.8"). Таким образом, нет никаких требований, чтобы он был одинаковым на разных платформах, не говоря уже о переносимости.

Другие вопросы по тегам