Нет, это невозможно, по крайней мере с тем, как работает традиционная цепочка инструментов.Традиционный порядок операций заключается в том, что вся компиляция выполняется, а затем выполняется компоновка.
Чтобы сгенерировать встроенную функцию сравнения, компилятору сначала необходимо сгенерировать код для qsort
самого встроенного (поскольку каждый экземплярqsort
обычно будет использовать другую функцию сравнения).Однако в случае чего-то вроде qsort
оно обычно компилируется и помещается в стандартную библиотеку еще до того, как вы начинаете думать о написании своего кода.Когда вы компилируете свой код, qsort
доступен только как объектный файл.
Таким образом, чтобы иметь возможность сделать что-то подобное, вам нужно встроить возможность встраивания в компоновщик , а не компилятор.По крайней мере, в теории это возможно, но это определенно нетривиально - по крайней мере, по моей оценке, это почти наверняка сложнее, чем при работе с исходным кодом.Это также требует дублирования совсем некоторой функциональности, подобной компилятору, в компоновщике, и , вероятно, требует добавления достаточного количества дополнительной информации в объектный файл, чтобы дать компоновщику достаточно информации для работы, с которой он может даже попробоватьчтобы выполнить работу.
Редактировать: возможно, мне следует углубиться в подробности, чтобы цепочка комментариев не превратилась в полноценный аргумент чуть больше, чем формулировка.
Традиционно компоновщик принципиально довольно простой вид зверя.Он начинается с объектного файла, который можно разделить на четыре основных элемента:
- Набор битов, которые должны быть скопированы (без изменений, если не указано иное) из объектного файла в создаваемый исполняемый файл..
- Список символов, содержащийся в объектном файле.
- Список символов, которые не включены в объектный файл.
- Список исправлений, для которых требуется адресбыть записанным.
Затем компоновщик начинает сопоставлять символы, экспортированные в один файл и используемые в другом.Затем он просматривает объектные файлы в библиотеке (или библиотеках) для разрешения большего количества символов.Каждый раз, когда он добавляет в файл, он также добавляет свой список необходимых символов и рекурсивно ищет другие объектные файлы, которые могут их удовлетворить.
Когда он находит объектные файлы, которые предоставляют всеСимволы, он копирует набор битовых частей каждого из них в выходной файл, и, где записи исправления говорят об этом, он записывает относительные адреса, назначенные определенным символам (например, где вы назвали printf
, он вычисляетгде в исполняемом файле он скопировал биты, составляющие printf
, и заполнил ваш вызов этим адресом).В относительно недавних случаях вместо того, чтобы копировать биты из библиотеки, он может вставить ссылку на общий объект / DLL в исполняемый файл и предоставить его загрузчику для фактического поиска / загрузки этого файла во время выполнения для предоставления фактического кода длясимвол.
В частности, однако, компоновщик традиционно игнорирует фактическое содержимое блоков битов, которые он копирует.Вы можете (например) вполне разумно использовать один и тот же компоновщик для работы с кодом для любого из множества различных процессоров.Пока все они используют одинаковые форматы объектов и исполняемых файлов, это нормально.
Оптимизация времени соединения действительно меняет это, по крайней мере, до некоторой степени.Очевидно, что для оптимизации кода нам нужен какой-то дополнительный интеллект, который происходит в то, что традиционно считалось временем соединения.Есть (по крайней мере) два способа сделать это:
- встроить в компоновщик довольно много дополнительного интеллекта
- сохранить интеллект в компиляторе и заставить его вызывать компоновщиксделать оптимизацию.
Есть примеры обоих из них - LLVM (для одного очевидного примера) в значительной степени берет первое. Фронтальный компилятор испускает коды LLVM, а LLVM вкладывает много интеллекта / работы в его преобразование в оптимизированный исполняемый файл. gcc с GIMPLE выбирает последний путь: записи GIMPLE в основном дают компоновщику достаточно информации, чтобы он мог передать биты в ряде объектных файлов обратно компилятору, заставить компилятор оптимизировать их, а затем передать результат обратно компоновщику для фактически скопировать в исполняемый файл.
Полагаю, вы, вероятно, можете придумать какую-то философскую точку зрения, которая говорит, что эти два в основном эквивалентны - но я сомневаюсь, что любой, кто реализовал оба, согласится.
Теперь верно (возможно, в любом случае), что одного из них будет достаточно для осуществления оптимизации под рукой. Лично я сомневаюсь, что кто-то реализует эту оптимизацию для себя. Когда вы приступите к этому, qsort
и bsearch
- это почти единственные две разумно общие функции, к которым он будет / будет применяться. Для большинства практических целей это означает, что вы будете осуществлять оптимизацию исключительно ради qsort
.
С другой стороны, если задействованные инструменты включают в себя возможность создавать встроенные функции и оптимизацию времени соединения, то я полагаю, что, по крайней мере, есть разумный шанс, что этот тип оптимизации может закончиться как более или менее случайный побочный эффект того, что они собираются вместе.
По крайней мере, теоретически это означает, что это может произойти. Однако есть еще одна проблема: совершенно независимо от оптимизации, многие компиляторы будут , а не генерировать встроенный код для рекурсивной функции. Чтобы даже попытаться это сделать, компилятор должен сначала преобразовать рекурсивную функцию в итеративную форму. Это довольно распространено в случае хвостовой рекурсии, но быстрая сортировка не является хвостовой рекурсией. Почти единственной альтернативой является реализация qsort
, которая не является рекурсивной для начала. Это, конечно, возможно, но столь же необычно.
Таким образом, даже когда / если набор инструментов может поддерживать встроенную генерацию обратного вызова, это, вероятно, не будет в случае qsort
(что, я признаю, является единственным случаем Я лично проверял). Однако, к лучшему или худшему, qsort
- почти единственная функция такого рода, которая достаточно распространена, чтобы она также имела большое значение.