Инструменты разработки приложений для мобильных устройств характеризуются различными уровнями поддержки стандартов ANSI C/C++. Если вы хотите обеспечить переносимость кода или библиотек, придерживайтесь следующих рекомендаций:
• Тщательно выясняйте уровень поддержки стандартов, который обеспечивается используемыми вами компиляторами, обращая особое внимание на такие тонкие вещи, как структурная обработка исключений. Различные компиляторы предоставляют различные уровни поддержки таких, например, средств, как обработка исключений, а возможно, и таких вещей, как операции с вещественными числами или стандартная битовая ширина целых чисел.
• Обзаведитесь хорошим руководством по программированию, содержащим подробные описания всех средств компилятора и библиотек программ, которые вы будете использовать в своих приложениях. Убедитесь в том, что эти средства и библиотеки поддерживаются всеми компиляторами, которые вы планируете использовать для различных целевых процессоров или операционных систем.
• Рассмотрите возможность использования "наименьшего общего знаменателя" для всей совокупности возможных средств, например, ограничьтесь применением только языка С (в смысле — откажитесь от С++) или же используйте лишь какое-то отдельное простое подмножество средств С++. Благодаря этому вы сможете быть уверены в том, что присутствие и поддержка выбранных вами языковых средств будут в равной степени обеспечиваться широким кругом компиляторов.
• Как можно чаще и начиная уже с ранних стадий разработки, тестируйте приложение на каждом компиляторе/платформе, для работы с которыми оно запланировано. Качество кода, обеспечиваемое различными компиляторами C/C++, не обязательно одинаково, и некоторые их них могут содержать ошибки, которые могут быть выявлены только в процессе выполнения или отладки программы. В целом, компиляторы C/C++ для мобильных устройств используются далеко не так широко, как компиляторы для настольных компьютеров и серверов, работающих на процессорах семейства х86. Как следствие, число разработчиков, рискующих испытать свои силы на поприще генерации кодов для них, не так уж и велико. Как показала практика, чем менее широко используется какой-либо программный продукт, тем больше скрытых ошибок в нем содержится и тем больше ограничений ему свойственно. Не утруждая себя частым тестированием кодов на протяжении всего периода разработки с использованием для этого всей совокупности компиляторов и платформ, для выполнения на которых предназначено приложение, вы заведомо готовите для себя неприятные сюрпризы в будущем!
• Тщательно ознакомьтесь с описанными в соответствующих лицензионных соглашениях ограничениями, касающимися использования применяемых вами компиляторов библиотек и инструментальных средств. Если вы создаете приложение, предназначенное для коммерческого использования, изучите лицензионные соглашения всех без исключения компиляторов, библиотек исходных кодов и библиотек времени выполнения, а также средств компоновки, которые вами используются. Каким бы утомительным ни было чтение этих документов, лучше быть хорошо осведомленным об этих ограничениях уже с самого начала, чем переделывать всю работу или переходить на другой компилятор на более поздних стадиях производственного цикла. Смена компилятора может казаться легкой, но повозиться вам придется немало, и вдобавок это отнимет много времени. Все сказанное выше справедливо в отношении любого программного обеспечения разработчика, но в отношении средств разработки приложений для мобильных устройств это справедливо вдвойне из-за огромного разнообразия специализированных инструментальных средств, сопровождаемых лицензионными соглашениями самых различных видов, включая EULA (end-user license agreement — лицензионное соглашение с конечным пользователем), ограничения, касающиеся лицензионных платежей, ограничения, касающиеся защиты прав на интеллектуальную собственность, например, GPL (general public license — общедоступная лицензия), LGPL (lesser general public license — общедоступная лицензия с ограничениями), FreeBSD и тому подобное.
Caveat emptor[1]; Пусть программист будет бдителен! Никто никого не запугивает; это только призыв к тому, чтобы, делая свой выбор, вы поступали осмотрительно.
Разработка приложений для мобильных устройств с использованием собственных кодов не является чем-то необычным и вполне может соответствовать вашим потребностям, но решение об этом вы должны принимать осознанно, понимая причины, побуждающие вас к такому выбору. Если при создании мобильного приложения вы решаете прибегнуть к написанию собственных кодов, убедитесь в том, что для этого имеются веские причины, и не пожалейте времени на то, чтобы заранее продумать, какие возможности компиляторов C/C++ вам могут для этого понадобиться и какие усилия вам придется приложить для преодоления возникающих при этом проблем разработки и отладки. Разрабатывать мобильные приложения на основе собственного кода намного сложнее, чем для настольных компьютеров или серверов. Чтобы добиться в этом успеха, вы должны быть вооружены ясным пониманием целей и знаниями инструментальных средств, которые собираетесь для этого использовать.
Термин "управляемый код" (managed code) относится к программному коду, выполняющемуся в управляемой среде (managed environment), будь то среда сервера, персонального компьютера, мобильного устройства или встроенной системы. Диспетчер среды времени выполнения (runtime engine), или просто —среды выполнения, отвечает за распределение ресурсов, управление выполнением потоков и необходимую синхронизацию, а также обеспечивает безопасность типов выполняющегося кода, предотвращая несанкционированный доступ к памяти. Этот уровень абстракции располагается выше уровня собственного кода, что позволяет значительно повысить производительность труда разработчиков и надежность кода. Время существования объектов и других типов, размещенных в памяти выполняющимся кодом, отслеживается диспетчером среды выполнения, что избавляет разработчика от необходимости решения этой задачи. В результате компиляции управляемого кода генерируются двоичные коды инструкций, которые включают в себя метаданные с подробными описаниями классов, типов, переменных и другую информацию, необходимую для управления выполнением кода. Содержащиеся в метаданных описания кодов диспетчер среды выполнения использует для реализации своих административных и контрольных функций. Именно богатый набор метаданных и является ключевым отличием управляемого кода от собственных кода. К числу других характеристик, являющихся общими для многих управляемых сред выполнения, относятся следующие:
■ Независимость от процессора. При компиляции программы, написанной с использованием управляемого кода, получаются не специфические для процессора машинные команды, а программа на промежуточном языке. Для промежуточного языка (intermediate language) часто используют сокращение IL, а в некоторых средах времени выполнения его называют "байтовыми кодами" ("byte codes"); оба эти термина имеют один и тот же смысл. Впоследствии этот промежуточный код преобразуется в мобильном устройстве в соответствующий формат исполняемого кода. Компиляция программы в формат IL обеспечивает возможность выполнения одного и того же скомпилированного кода не только на различных процессорах, но и с использованием адресов различного размера. Так, один и тот же IL-компонент может выполняться и на 32-, и на 64-разрядных процессорах, поскольку инструкции не зависят от размера адресных полей процессора.
■ Независимость от операционной системы. Среды выполнения управляемого кода вместе с их библиотеками обеспечивают разработчикам возможность написания программ на уровне абстракции, расположенном поверх базовой операционной системы. Учитывая тот факт, что пользовательские интерфейсы и модели взаимодействия с пользователем для классов устройств, в которых применяются различные степени абстрагирования верхних уровней, значительно отличаются друг от друга, принцип разработки программ "пишется однажды, выполняется везде" ("write once, run everywhere") вряд ли можно считать практически осуществимым, однако операционная система все еще остается весьма полезным средством обеспечения переносимости приложений на устройства разных классов. Кроме того, возможность создавать автономные (автономные (headless) — не имеющие пользовательского интерфейса) компоненты, способные выполняться на различных устройствах без перекомпиляции, оказывается очень полезной при построении повторно используемых модулей. После того как автономные компоненты заполнены общим кодом, остается лишь реализация зависящих от конкретного типа устройства пользовательских интерфейсов, в которых используются общие модули такого типа.