Я изучаю C ++, и я только начал изучать некоторые возможности Qt для программирования программ с графическим интерфейсом. Я задал себе следующий вопрос:
Как C ++, который ранее не имел синтаксиса, способного запрашивать у ОС окно или способ связи через сети (с API, которые я не совсем понимаю, я признаю), внезапно получил такие возможности через библиотеки, написанные на C ++? Мне все кажется ужасно круглым. Какие инструкции C ++ вы могли бы придумать в этих библиотеках?
Я понимаю, что этот вопрос может показаться тривиальным для опытного разработчика программного обеспечения, но я искал несколько часов, не находя прямого ответа. Дошло до того, что я не могу следовать руководству по Qt, потому что существование библиотек мне непонятно.
c++
libraries
bootstrapping
Мед Ларби Сентисси
источник
источник
Ответы:
Компьютер подобен луку, у него много слоев, от внутреннего ядра чистого оборудования до самого внешнего прикладного уровня. Каждый слой предоставляет свои части следующему внешнему слою, так что внешний слой может использовать некоторые функциональные возможности внутренних слоев.
В случае, например, Windows, операционная система предоставляет так называемый WIN32 API для приложений, работающих в Windows. Библиотека Qt использует этот API для предоставления приложениям, использующим Qt, свой собственный API. Вы используете Qt, Qt использует WIN32, WIN32 использует более низкие уровни операционной системы Windows и так далее, пока не появятся электрические сигналы в оборудовании.
источник
Qt
здесь приведена абстракция нижележащего слоя, потому что в LinuxQt
вызывается API Linux, а не WIN32 API.user32.dll
, или возможноgdi32.dll
.Вы правы в том, что в целом библиотеки не могут сделать ничего невозможного.
Но библиотеки не должны быть написаны на C ++, чтобы их можно было использовать в программе на C ++. Даже если они написаны на C ++, они могут использовать другие библиотеки, не написанные на C ++. Так что тот факт, что C ++ не предоставил никакого способа сделать это, не препятствует его добавлению, если есть какой-то способ сделать это вне C ++.
На довольно низком уровне некоторые функции, вызываемые C ++ (или C), будут написаны на ассемблере, и сборка содержит необходимые инструкции для выполнения того, что не возможно (или не просто) в C ++, например, для вызова системная функция. В этот момент этот системный вызов может сделать все , на что способен ваш компьютер, просто потому, что его ничто не остановит.
источник
C и C ++ имеют 2 свойства, которые позволяют всю эту расширяемость, о которой говорит OP.
В ядре или в базовой платформе в незащищенном режиме периферийные устройства, такие как последовательный порт или дисковод, отображаются на карту памяти так же, как и ОЗУ. Память - это серия переключателей, и переключение периферийных устройств (таких как последовательный порт или драйвер диска) заставляет ваше периферийное устройство выполнять полезные функции.
В операционной системе защищенного режима, когда кто-то хочет получить доступ к ядру из пространства пользователя (скажем, при записи в файловую систему или нарисовать пиксель на экране), необходимо выполнить системный вызов. C не имеет инструкции для выполнения системных вызовов, но C может вызвать ассемблерный код, который может вызвать правильный системный вызов. Это то, что позволяет коду C общаться с ядром.
Чтобы упростить программирование конкретной платформы, системные вызовы заключаются в более сложные функции, которые могут выполнять некоторые полезные функции в собственной программе. Можно свободно вызывать системные вызовы напрямую (с использованием ассемблера), но, вероятно, проще просто использовать одну из функций-оболочек, которые предоставляет платформа.
Существует еще один уровень API, который намного полезнее системного вызова. Взять, к примеру, malloc. Это не только вызовет систему для получения больших блоков памяти, но и будет управлять этой памятью, ведя весь бухгалтерский учет того, что происходит.
Win32 API обертывают некоторые графические функции общим набором виджетов платформы. Qt продвигается дальше, оборачивая Win32 (или X Windows) API кроссплатформенным способом.
Фундаментально, хотя компилятор C превращает код C в машинный код, и, поскольку компьютер предназначен для использования машинного кода, следует ожидать, что C сможет выполнить общий доступ lions или то, что может сделать компьютер. Все, что делают библиотеки-обертки, это делает тяжелую работу за вас, чтобы вам не пришлось это делать.
источник
Языки (например, C ++ 11 ) - это спецификации на бумаге, обычно написанные на английском языке. Загляните в последнюю версию C ++ 11 (или купите дорогостоящую спецификацию у вашего поставщика ISO).
Обычно вы используете компьютер с какой-либо языковой реализацией (в принципе, вы можете запустить программу на C ++ без какого-либо компьютера, например, используя для интерпретации несколько рабов-людей; это было бы неэтично и неэффективно)
Ваша реализация C ++ в целом работает над некоторой операционной системой и взаимодействует с ней (используя некоторый специфичный для реализации код, часто в какой-то системной библиотеке). Обычно эта связь осуществляется через системные вызовы . Посмотрите, например, в syscalls (2) список системных вызовов, доступных в ядре Linux .
С прикладной точки зрения системный вызов - это элементарная машинная инструкция, подобная
SYSENTER
x86-64, с некоторыми соглашениями ( ABI ).На моем рабочем столе Linux библиотеки Qt выше клиентских библиотек X11, взаимодействующих с сервером X11 через протоколы Xorg через X Windows .
В Linux используйте
ldd
свой исполняемый файл, чтобы увидеть (длинный) список зависимостей от библиотек. Используйтеpmap
в своем рабочем процессе, чтобы увидеть, какие из них «загружены» во время выполнения. Кстати, в Linux ваше приложение, вероятно, использует только бесплатное программное обеспечение, вы можете изучить его исходный код (от Qt до Xlib, libc, ... ядра), чтобы лучше понять, что происходитисточник
Я думаю, что вам не хватает концепции системных вызовов . Каждая операционная система предоставляет огромное количество ресурсов и функциональных возможностей, которые вы можете использовать для выполнения низкоуровневых операций, связанных с операционной системой. Даже когда вы вызываете обычную библиотечную функцию, она, вероятно, выполняет системный вызов за кулисами.
Системные вызовы - это низкоуровневый способ использования возможностей операционной системы, но он может быть сложным и громоздким в использовании, поэтому его часто «оборачивают» в API, чтобы вам не приходилось иметь с ними дело напрямую. Но под всем, что бы вы ни делали, включая ресурсы, связанные с O / S, будут использоваться системные вызовы, включая печать, сетевые подключения, сокеты и т. Д.
В случае Windows Microsoft Windows имеет свой GUI, фактически записанный в ядро, поэтому существуют системные вызовы для создания окон, рисования графики и т. Д. В других операционных системах GUI может не являться частью ядра, и в этом случае насколько я знаю, не было бы никаких системных вызовов для вещей, связанных с GUI, и вы могли бы работать только на еще более низком уровне с любой доступной графикой низкого уровня и входными связанными вызовами.
источник
Хороший вопрос. Каждый новый разработчик C или C ++ имеет это в виду. Я предполагаю стандартную машину x86 для остальной части этого поста. Если вы используете компилятор Microsoft C ++, откройте блокнот и введите его (назовите файл Test.c)
А теперь скомпилируйте этот файл (используя командную строку разработчика) cl Test.c /FaTest.asm
Теперь откройте Test.asm в своем блокноте. Вы видите переведенный код - C / C ++ переведен на ассемблер. Ты понял намек?
Программы на C / C ++ предназначены для работы на металле. Это означает, что они имеют доступ к оборудованию более низкого уровня, что облегчает использование возможностей оборудования. Скажем, я собираюсь написать библиотеку C getch () на компьютере с архитектурой x86.
В зависимости от ассемблера я мог бы напечатать что-нибудь таким образом:
Я запускаю его с помощью ассемблера и генерирую .OBJ - назовите его getch.obj.
Затем я пишу программу на C (я ничего не включаю)
Теперь назовите этот файл - GetChTest.c. Скомпилируйте этот файл, передав getch.obj. (Или скомпилировать по отдельности в .obj и LINK GetChTest.Obj и getch.Obj вместе, чтобы создать GetChTest.exe).
Запустите GetChTest.exe, и вы обнаружите, что он ожидает ввода с клавиатуры.
Программирование на C / C ++ - это не только язык. Чтобы быть хорошим программистом на C / C ++, вы должны хорошо понимать тип машины, на которой он работает. Вам нужно будет знать, как осуществляется управление памятью, как структурированы регистры и т. Д. Возможно, вам не понадобится вся эта информация для обычного программирования, но они очень вам помогут. Помимо базовых знаний об оборудовании, это, безусловно, поможет, если вы поймете, как работает компилятор (то есть, как он переводит) - что может позволить вам настроить свой код по мере необходимости. Это интересная упаковка!
Оба языка поддерживают ключевое слово __asm, что означает, что вы также можете смешивать код на ассемблере. Изучение C и C ++ сделает вас лучшим программистом в целом.
Нет необходимости всегда связываться с Ассемблером. Я упоминал об этом, потому что думал, что это поможет вам лучше понять. В основном, большинство таких библиотечных вызовов используют системные вызовы / API, предоставляемые операционной системой (операционная система, в свою очередь, выполняет взаимодействие с оборудованием).
источник
Нет ничего волшебного в использовании других библиотек. Библиотеки - это простые большие пакеты функций, которые вы можете вызывать.
Считайте, что вы пишете такую функцию
Теперь, если вы включите этот файл, вы можете написать
addExclamation(myVeryOwnString);
. Теперь вы можете спросить: «Как в C ++ вдруг появилась возможность добавлять восклицательные знаки в строку?» Ответ прост: вы написали функцию для этого, а затем вызвали ее.Итак, чтобы ответить на ваш вопрос о том, как C ++ может получить возможность рисовать окна через библиотеки, написанные на C ++, ответ тот же. Кто-то еще написал функцию (и) для этого, а затем скомпилировал их и передал вам в виде библиотеки.
Другие вопросы отвечают за то, как на самом деле работает рисование окон, но вы были озадачены тем, как работают библиотеки, поэтому я хотел затронуть наиболее фундаментальную часть вашего вопроса.
источник
Ключ - это возможность операционной системы предоставлять API и подробное описание того, как использовать этот API.
Операционная система предлагает набор API с соглашениями о вызовах. Соглашение о вызовах определяет способ, которым параметр передается в API, и как возвращаются результаты, и как выполнить фактический вызов.
Операционные системы и компиляторы, создающие для них код, прекрасно сочетаются друг с другом, поэтому обычно вам не нужно думать об этом, просто используйте его.
источник
Нет необходимости в специальном синтаксисе для создания окон. Все, что требуется, - это то, что ОС предоставляет API для создания окон. Такой API состоит из простых вызовов функций, для которых C ++ предоставляет синтаксис.
Кроме того, C и C ++ являются так называемыми языками системного программирования и могут получать доступ к произвольным указателям (которые могут быть привязаны к некоторому устройству аппаратным обеспечением). Кроме того, также довольно просто вызывать функции, определенные в сборке, что позволяет выполнять весь спектр операций, предоставляемых процессором. Поэтому можно написать саму ОС, используя C или C ++ и небольшое количество сборок.
Также следует отметить, что Qt является плохим примером, поскольку он использует так называемый мета-компилятор для расширения синтаксиса C ++. Это, однако, не связано с его способностью вызывать API, предоставляемые ОС, чтобы фактически рисовать или создавать окна.
источник
Во-первых, немного недоразумений, я думаю,
Нет синтаксиса для выполнения операций ОС. Это вопрос семантики .
Итак, операционная система написана в основном на C. Вы можете использовать разделяемые библиотеки (так, dll) для вызова внешнего кода. Кроме того, код операционной системы может регистрировать системные подпрограммы на системные вызовы * или прерывания, которые можно вызывать с использованием сборки. . Эти разделяемые библиотеки часто просто делают системные вызовы для вас, поэтому вы избавлены от использования встроенной сборки.
Вот хорошее руководство по этому вопросу: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Это для Linux, но принципы те же.
Как операционная система выполняет операции с графическими картами, сетевыми картами и т. Д.? Это очень широкая тема, но в основном вам нужен доступ к прерываниям, портам или записи некоторых данных в специальную область памяти. Поскольку эти операции защищены, вам все равно нужно вызывать их через операционную систему.
источник
В попытке представить несколько иной взгляд на другие ответы я отвечу так.
(Отказ от ответственности: я немного упрощаю вещи, ситуация, которую я представляю, является чисто гипотетической и написана как средство демонстрации концепций, а не на 100% верная жизни).
Подумайте о вещах с другой точки зрения, представьте, что вы только что написали простую операционную систему с базовыми возможностями создания потоков, управления окнами и памятью. Вы хотите реализовать библиотеку C ++, чтобы позволить пользователям программировать на C ++ и делать такие вещи, как создание окон, рисование на окнах и т. Д. Вопрос в том, как это сделать.
Во-первых, поскольку C ++ компилируется в машинный код, вам необходимо определить способ использования машинного кода для взаимодействия с C ++. Здесь функции приходят, функции принимают аргументы и возвращают значения, таким образом, они обеспечивают стандартный способ передачи данных между различными разделами кода. Они делают это, создавая нечто, известное как соглашение о вызовах .
А соглашение о вызовах государства , где и как аргументы должны быть размещены в памяти , так что функция может найти их , когда он запускается на выполнение. Когда функция вызывается, вызывающая функция помещает аргументы в память, а затем просит ЦПУ перейти к другой функции, где она делает то, что делает, прежде чем вернуться к тому месту, откуда она была вызвана. Это означает, что вызываемый код может быть абсолютно любым, и это не изменит способ вызова функции. В этом случае, однако, код функции должен относиться к операционной системе и работать во внутреннем состоянии операционной системы.
Итак, спустя много месяцев, и у вас все функции ОС разобраны. Ваш пользователь может вызывать функции для создания окон и рисования на них, они могут создавать темы и всякие замечательные вещи. Но вот в чем проблема, функции вашей ОС будут отличаться от функций Linux или Windows. Таким образом, вы решаете, что вам нужно предоставить пользователю стандартный интерфейс, чтобы он мог писать переносимый код. Вот где приходит QT.
Как вы почти наверняка знаете, в QT есть множество полезных классов и функций для выполнения задач, которые делают операционные системы, но способом, который кажется независимым от базовой операционной системы. Это работает так, что QT предоставляет классы и функции, которые одинаковы в том виде, в котором они отображаются пользователю, но код этих функций различен для каждой операционной системы. Например, QT QApplication :: closeAllWindows () будет фактически вызывать специализированную функцию закрытия окна каждой операционной системы в зависимости от используемой версии. В Windows он, скорее всего, будет вызывать CloseWindow (hwnd), тогда как в ОС, использующей X Window System, он потенциально будет вызывать XDestroyWindow (display, window).
Как видно, операционная система имеет много уровней, каждый из которых должен взаимодействовать через интерфейсы многих разновидностей. Есть много аспектов, которые я даже не затронул, но объяснение их всех заняло бы очень много времени. Если вас больше интересует внутренняя работа операционных систем, я рекомендую проверить OS dev wiki .
Однако имейте в виду, что причина, по которой многие операционные системы предпочитают предоставлять интерфейсы для C / C ++, заключается в том, что они компилируются в машинный код, они позволяют смешивать инструкции по сборке со своим собственным кодом и предоставляют большую степень свободы программисту.
Опять же, здесь много чего происходит. Я хотел бы продолжить, чтобы объяснить, как библиотеки, такие как .so и .dll файлы не должны быть написаны на C / C ++ и могут быть написаны на ассемблере или других языках, но я чувствую, что если я добавлю больше, я мог бы также написать целую статью, и как бы мне этого не хотелось, у меня нет сайта для ее размещения.
источник
Когда вы пытаетесь нарисовать что-то на экране, ваш код вызывает какой-то другой фрагмент кода, который вызывает другой код (и т. Д.), Пока, наконец, не произойдет «системный вызов», который является специальной инструкцией, которую может выполнить ЦП. Эти инструкции могут быть либо написаны на ассемблере, либо написаны на C ++, если компилятор поддерживает их «встроенные функции» (которые являются функциями, которые компилятор обрабатывает «специально» путем преобразования их в специальный код, понятный ЦП). Их задача - заставить операционную систему что-то делать.
Когда происходит системный вызов, вызывается функция, которая вызывает другую функцию (и т. Д.), Пока, наконец, драйвер дисплея не скажет нарисовать что-то на экране. В этот момент драйвер дисплея просматривает конкретную область в физической памяти, которая на самом деле является не памятью, а диапазоном адресов, в который можно записать, как если бы это была память. Вместо этого, однако, запись в этот диапазон адресов заставляет графическое оборудование перехватывать запись в память и рисовать что-то на экране.
Запись в эту область памяти - это то, что может. кодировать в C ++, поскольку на стороне программного обеспечения это просто обычный доступ к памяти. Просто аппаратное обеспечение обрабатывает это по-другому.
Так что это действительно базовое объяснение того, как это может работать.
источник
syscall
(и его двоюродный братsysenter
) действительно инструкция процессора.sysenter
являются оптимизированными путями вызовов, поскольку переключение контекста, используемое обработчиками прерываний, было не таким быстрым, как всем хотелось бы, но, по сути, это все еще программно сгенерированное прерывание, в то время как оно обрабатывается путем векторизации в обработчик, установленный ядром ОС. Часть процесса переключения контекста, который выполняется IS,sysenter
заключается в изменении битов режима в процессоре, чтобы установить кольцо 0 - полный доступ ко всем привилегированным инструкциям, регистрам и областям памяти и ввода-вывода.Ваша программа на C ++ использует библиотеку Qt (также написанную на C ++). Библиотека Qt будет использовать функцию Windows CreateWindowEx (которая была написана на C внутри kernel32.dll). Или в Linux он может использовать Xlib (также написанный на C), но он также может посылать необработанные байты, которые в протоколе X означают « Пожалуйста, создайте окно для меня ».
С вашим вопросом catch-22 связано историческое примечание о том, что «первый компилятор C ++ был написан на C ++», хотя на самом деле это был компилятор C с несколькими понятиями C ++, достаточно, чтобы он мог скомпилировать первую версию, которая затем могла скомпилировать себя ,
Аналогично, компилятор GCC использует расширения GCC: сначала он компилируется в версию, а затем используется для перекомпиляции. (Инструкции по сборке GCC)
источник
Как я вижу вопрос, это на самом деле вопрос компилятора.
Посмотрите на это так, вы пишете кусок кода на ассемблере (вы можете сделать это на любом языке), который переводит ваш вновь написанный язык, который вы хотите назвать Z ++, в ассемблер, для простоты давайте назовем его компилятором (это компилятор) ,
Теперь вы даете этому компилятору некоторые базовые функции, чтобы вы могли писать int, string, arrays и т. Д. На самом деле вы предоставляете ему достаточно возможностей, чтобы вы могли писать сам компилятор на Z ++. и теперь у вас есть компилятор для Z ++, написанный на Z ++, довольно аккуратно, верно.
Что еще круче, так это то, что теперь вы можете добавлять способности к этому компилятору, используя уже имеющиеся у него возможности, таким образом расширяя язык Z ++ новыми функциями, используя предыдущие функции.
Например, если вы напишите достаточно кода, чтобы нарисовать пиксель любого цвета, вы можете расширить его, используя Z ++, чтобы нарисовать все, что вы захотите.
источник
Аппаратное обеспечение - это то, что позволяет этому случиться. Вы можете думать о графической памяти как о большом массиве (состоящем из каждого пикселя на экране). Чтобы нарисовать на экране, вы можете написать в эту память, используя C ++ или любой другой язык, который позволяет прямой доступ к этой памяти. Эта память просто доступна или находится на видеокарте.
В современных системах прямой доступ к графической памяти потребует написания драйвера из-за различных ограничений, поэтому вы используете косвенные средства. Библиотеки, которые создают окно (на самом деле просто изображение, как любое другое изображение), а затем записывают это изображение в графическую память, которая затем отображается на экране графическим процессором. Ничего не нужно добавлять к языку, кроме возможности записи в определенные области памяти, для чего нужны указатели.
источник