Сегодня существует огромное количество различных платформ, под которые можно разрабатывать программное обеспечение. Однако не хочется писать программу индивидуально под каждую платформу. Значительно лучше организовать код таким образом, чтобы большая часть функционала была абстрагирована от платформо-зависимого кода.
Этот код не будет использовать низкоуровневые функции, специфичные какой либо из платформ, он будет одинаковый для любой платформы. Это позволит писать и отлаживать важный функционал один раз, а затем повторно использовать его во всех реализациях данной программы, под различные платформы.
Другая часть кода будет представлять собой обертку над платформо-зависимым функционалом. И именно об способах организации кода для этой обертки и пойдет речь в данной статье.
Задача следующая: абстрагировать платформо-зависимую функциональность за общим интерфейсом, который будет использовать весь остальной код.
Для достижения данной цели, существует ряд различных подходов. Однако сначала рассмотрим средства, которые предоставляет язык С++ для этого.
Средства языка
В первую очередь нужно в коде определить, какая текущая платформа. Для этого удобно создать макросы, по которым можно будет определять платформу. Разумеется, в версии под конкретную платформу будет определен только один макрос. Их можно определить в базовом заголовочном файле или в настройках компиляции проекта. Допустим у нас будет две платформы со следующими макросами:
1
2
3
#define VERSION_WIN32 1 - windows
#define VERSION_PS3 1 - PlayStation3
...
Теперь можно использовать эти макросы для включения кода для конкретной платформы, например так:
1
2
3
4
5
6
7
8
9
#ifdef VERSION_WIN32
// этот код будет выполняться только в версии программы, которая собирается для WIN32.
// для этой версии определен макрос VERSION_WIN32
#end
#if define( VERSION_PS3 ) - другой способ
// этот код будет выполняться, если VERSION_PS3 определен и не 0.
// этот код будет выполняться в версии программы, которая собирается для PlayStation3.
#end
Если нужно выполнить разный код, в зависимости от платформы, то можно воспользоваться директивой #elif. В этом случае рекомендуется выдавать ошибку компиляции, в случае если не определена ни одна платформа. Это вам пригодится при портировании программы под новую платформу. Когда вы попытаетесь скомпилировать программу вам тут же компилятор сообщит о тех местах в коде, которые нужно специализировать под все платформы.
1
2
3
4
5
6
7
#if defined( VERSION_WIN32 )
// ..
#elif defined( VERSION_PS3 )
// ..
#else
#error "Unsupported platform"
#end
Однако подобные макрос включения сильно загромождают код и делают его менее читабельным. Для устранения этого недостатка удобно определить следующие макросы:
1
2
3
4
5
6
7
8
9
#if defined( VERSION_WIN32 )
#define IsWin32() true
#define IsPs3() false
#elif defined( VERSION_PS3 )
#define IsWin32() false
#define IsPs3() true
#else
#error "Unsupported platform"
#end
Теперь можно использовать их в конструкциях if в любой точке кода. Компилятор в релизе вырежет ветки, для которых условия ложны, так что это ни как не отразится на размере и скорости выполнения кода. Зато будет значительно легче его отлаживать. Код будет более читабельным, исчезнут некрасивые макросы. Средства подсветки синтаксиса не будут стараться сделать код для другой платформы менее ярким, т.е. IDE станет меньше тормозить при переключение на другую платформу.
1
2
3
4
5
6
7
8
9
if( IsWin32() )
{
// этот код будет выполняться только для Win32 версии.
// на других платформах этой ветки кода не будет.
}
else if( IsPs3() )
{
}
...
Однако нужно помнить, что при таком написании нельзя в эти ветки включать код, который не определен для другой платформы. Программа в этом случае просто не с компилируется. Это идеальный вариант для организации различного поведения общего кода в зависимости от платформы.
Продолжение: