Кроссплатформенный код без препроцессора

Иван Шипицин
Иван Шипицин

Сейчас для написания кросплатформенного кода используется препроцессор, так как он умеет выкидывать ненужный код, но работает он только с определениями препроцессора.  С другой стороны, для условной компиляции в C++17 добавили if constexpr, но компилятор все равно анализирует оба пути выполнения (что не подходит для написания кроссплатформеного кода). Я предлагаю объединить эти два способа, и создать что-то вроде if constexpr static (...) {...} (синтаксис подлежит обсуждению).

Эта конструкция позволяет в качестве условия использовать любые компайл-тайм константы и игнорировать код под ложным условием.

void InitDriver() {
    if constexpr static (platform_name == "windows") { //platform_name - константа, определенная где-то в стандартной библиотеке
        INITIALIZE_DRIVER(); //некая функция WinApi
    } else if constexpr static (platform_name == "linux") {
        init_drv(); // некий системный вызов линукса
    } else {
        static_assert(false,"Unsupported OS")
    }
}

В данном примере, если бы код разбирался компилятором, даже если условие было ложным, то он бы не скомпилировался, жалуясь на функцию, не существующую в текущем окружении.

Впрочем, некоторое количество кода, огражденного препроцессором, не может быть скомпилировано. Например, непарные фигурные скобки. В данном случае компилятору будет логично считать, что после первой закрывающей скобки тело if constexpr static заканчивается и дальнейший текст не будет игнорироваться компилятором. В таком случае, требуется каким-то способом экранировать фигурные скобки. Например, писать само условие возле каждой закрывающей/открывающей фигурной скобки. Код начинает выглядеть громоздко, поэтому необходимо обсуждение для улучшения синтаксиса.

void InitDriver() {
    if constexpr static (platform_name == "windows") { //Код для виндовса сломан, и должен игнорироваться, иначе вызовет ошибки компиляции
        INITIALIZE_DRIVER();[; {
        } ** //на этой строке компилятору следовало бы остановиться, но нам этого не надо
        14 {] } 
        } @@ 554 $%(*
        8)qwerty 
    } (platform_name == "windows") else if constexpr static (platform_name == "linux") {
        init_drv(); 
    } (platform_name == "linux") else (platform_name == "linux") {
        static_assert(false,"Unsupported OS")
    } (platform_name == "linux")
}

Также, на код, находящийся в теле выражения, можно не создавать собственную область видимости (тем самым ещё больше приблизив эту конструкцию к дефайнам), что позволит создавать переменные, а затем использовать их даже в коде, находящемся вне конструкции условной компиляции.

void InitDriver() {
    if constexpr static (platform_name == "windows") {
        HANDLER h = GetPreInitHandler(); //находится в области видимости функции
    }
    Init();
    if constexpr static (platform_name == "windows") {
        PostInitWindowsWorkaround(h);
    }
    std::cout<<h; // компилируется на windows, но ругается на непонятную переменную на остальных системах
}
0
рейтинг
2 комментария
Antervis
для ОС-специфичных задач вам скорее всего придется инклюдить разные header'ы. Вообще, в случае с++ диспатчинг по ОС - скорее задача системы сборки
Antervis
yndx-antoshkka

Первоначальное предложение на constexpr if как раз позволяло такие конструкции. После обсуждения решено было запретить подобное, так как возникало множествво нерешаемых вопросов. Самое первое предложение по теме: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3322.pdf

В нём можно было делать подобное:

template< class T >
class C
{
    void common( ) { ... }

static_if( has_property1<T>() ) {
    void f1( ) { ... }
}

static_if( has_property2<T>() ) {
    void f2( ) { ... }
} else {
    void f2( ) = delete;
}

};
yndx-antoshkka
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения