Есть ли более элегантный способ использовать еще один оператор с независимыми if () - PullRequest
0 голосов
/ 13 мая 2018

В настоящее время я работаю в SFML для c ++ и пытаюсь изменить размер окна, однако решение, которое я нашел для моей проблемы, не совсем подходит, и я ищу, есть ли лучший способ.

У меня есть объект, размер которого несколько раз изменяется, поэтому он может уместиться внутри окна или нет, я должен расширить окно, если оно больше, и уменьшить его, если оно меньше. Однако у меня есть минимальный размер окна, если объект помещается внутрь, я хочу сбросить окно к этому размеру.

Это псевдокод, который я сейчас использую:

if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
    if(Object.getSize().x > windowMinSize.x){
        window.resize(Object.getSize().x, window.getSize().y)
    }
    if(Object.getSize().y > windowMinSize.y){
        window.resize(window.getSize().x, Object.getSize().y)
    }
}
else{
    window.resize(windowMinSize);
}

Я изучил switch и другие опции, но не нашел того, что искал.

Ответы [ 4 ]

0 голосов
/ 14 мая 2018

Я интерпретирую ваш вопрос как вопрос о том, как выполнить рефакторинг следующего кода:

if ( A )
{
     if ( B ) { X } else { Y }
     if ( C ) { Z } else { Y }
}
else { Y }

, чтобы избежать повторения Y.


Один из способов будет:

bool A = ....;
bool B = ....;
bool C = ....;

if      ( A && B ) { X }
else if ( A && C ) { Z }
else { Y }

хотя это может включать ненужные вызовы функций по сравнению с коротким замыканием в исходном коде.


Наиболее «очевидным» решением является хранение переменной:

bool updated = false;
if ( A ) 
{
    if ( B ) { X; updated = true; }
    if ( C ) { Z; updated = true; }
}
if ( !updated ) { Y }

Другой способ - использовать управляющую структуру, из которой вы можете выйти:

do
{
     if ( A )
     {
          if ( B ) { X; break; }
          if ( C ) { Z; break; }
     }
     Y;
} while (0);

Некоторым людям это не нравится, потому что если вы читаете код, похоже, что мы входим в цикл, но позже он оказывается не циклом. Лично я бы сделал функцию (с заменой break на return); но если вы используете do...while(0), вы должны поместить кодовый комментарий в начале, чтобы указать, что это не действительно цикл.

0 голосов
/ 14 мая 2018

Я немного сбит с толку, потому что в конце концов звучит так, будто вы хотите просто изменить размер окна до размера объекта, сохраняя при этом определенный минимальный размер.

Таким образом, все, что вам нужно, этопростой вызов std::max(), чтобы определить большее значение двух входных данных:

unsigned int width = std::max(min_width, object_width);
unsigned int height = std::max(min_height, object_height);

При использовании нового размера оно в основном изменит размер окна, чтобы соответствовать вашему объекту, если объект не меньше вашего минимального размера.

Вы можете использовать этот же шаблон, чтобы также добавить максимальный размер, используя std::min() в качестве дополнительного слоя:

unsigned int width = std::min(max_width, std::max(min_width, object_width));
unsigned int height = std::min(max_height, std::max(min_height, object_height));

Редактировать: Как правильно подсказал Болов, это можетупростите еще больше для современных компиляторов, использующих новые std::clamp:

unsigned int width = std::clamp(object_width, min_width, max_width);
unsigned int height = std::clamp(object_height, min_height, max_height);
0 голосов
/ 14 мая 2018

Вы можете поиграть с variadic template s, чтобы получить то, что вы хотите. Моя версия здесь принимает else функцию (первый аргумент), затем пары функций (предикат, блок, предикат, блок, ...). Если ни один из предикатов не оценивается как true, будет выполнена функция else. Это должно работать с C ++ 11 или новее.

Здесь могут быть некоторые ошибки (я не проводил много испытаний), но вы можете пнуть шины. Если вы измените #if 0 на #if 1, вы увидите, что функция else не будет вызываться.

#include <iostream>

template <typename PRED, typename EXEC>
bool multi_branch_detail(PRED pred, EXEC exec) {
    if(pred()) {
        exec();
        return true;
    }
    return false;
}

template <typename PRED, typename EXEC, typename ...REM>
bool multi_branch_detail(PRED pred, EXEC exec, REM ...rem) {
    auto result = false;
    if(pred()) {
        exec();
        result = true;
    }
    return multi_branch_detail(std::forward<REM>(rem)...) || result;
}

template <typename ELSE, typename ...FNS>
void multi_branch(ELSE el, FNS ...fns) {
    if(!multi_branch_detail(std::forward<FNS>(fns)...)) {
        el();
    }
}


int main() {
    multi_branch(
        []() { std::cout << "No cases\n"; },

#if 0
        []() { return 1 < 2; }, []() { std::cout << "first case\n"; },
        []() { return 10 < 20; }, []() { std::cout << "second case\n"; },
#endif
        []() { return 1 > 2; }, []() { std::cout << "bug\n"; }
    );
    return 0;
}

Вывод с #if 0:

No cases

Вывод с #if 1:

first case
second case
0 голосов
/ 13 мая 2018

Правильный шаблон для использования здесь - это ограничение количества путей кода, которые вызывают внешний метод, так что существует единственная точка использования:

Ваш псевдокод становится примерно таким:

auto new_win_size = windowMinSize;
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
    if(Object.getSize().x > windowMinSize.x){
        new_win_size.x = Object.getSize().x;
        new_win_size.y = window.getSize().y;
    }
    if(Object.getSize().y > windowMinSize.y){
        new_win_size.x = window.getSize().x;
        new_win_size.y = Object.getSize().y;
    }
}

window.resize(new_win_size );

Но я подозреваю, что вы на самом деле хотите:

auto new_win_size = windowMinSize;

if(Object.getSize().x > windowMinSize.x){
    new_win_size.x = Object.getSize().x;
}
if(Object.getSize().y > windowMinSize.y){
    new_win_size.y = Object.getSize().y;
}

window.resize(new_win_size );
...