Прерывание -возможности именованных областей в C/C++

Введение

Этот вопрос следует из этого:Идиома названного цикла :опасна? . Для тех, кто не хочет читать исходный вопрос, речь шла о таких вещах:

named(label1) for(int i = 0 ; i < 10 ; i++) {
    for(int j = 0 ; j < 10 ; j++) {
      if(some_condition)
            break(label1); // exit outer loop
      }
}

Этот новый вопрос касается улучшенной версии идиомы «именованный цикл». Если вам лень читать весь пост, вы можете сразу перейти к разделу «Пример» этого поста, чтобы четко понять, о чем я говорю.

Недостатки конструкции

К сожалению, вопрос был закрыт очень быстро (, а позже вновь -открыт ), потому что это были скорее дебаты за/против, чем чисто технический вопрос. Кажется, это не соответствовало формату SO Q&A. Более того, в представленном мной коде было несколько недостатков :

  • . Ключевое слово breakбыло переопределено макросом
  • Макрос был написан строчными буквами
  • Он сделал некоторые ужасные вещи компилируемыми (, по крайней мере, с использованием MSVC ):

    int foo() {
    
       named(label1) for(int i = 0 ; i < 10; i++)
       {
          if(some_condition)
          {
              break(label1);  // here it's ok, the behavior is obvious
          }   
       }
    
       break(label1); // it compiles fine without warning... but the behavior is pretty obscur!
    
    }
    
  • . Это может сломать хороший -код. Например, следующее не компилируется из-за проблемы с областью действия.

    int foo() {
    
    named(label1)  for(int i = 0 ; i < 10 ; i++)
           named(label2)  for(int j = 0 ; j < 10 ; j++)
            if(i*j<15)
                cout << i*j << endl;
            else
                break(label2);
     }
    

Более безопасная реализация

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

Вот определение двух макросов NAMEDиBREAK:

    #define NAMED(bn)   if(const bool _YOU_CANNOT_USE_NAMED_BREAK_IF_YOU_ARE_OUTSIDE_##bn##_ = true)\
                            goto _named_loop_identifier__##bn##_;\
                        else\
                            _break_the_loop_labelled_##bn##_:\
                            if(true)\
                                {}\
                            else\
                                if(! _YOU_CANNOT_USE_NAMED_BREAK_IF_YOU_ARE_OUTSIDE_##bn##_)\
                                    goto _break_the_loop_labelled_##bn##_;\
                                else\
                                    _named_loop_identifier__##bn##_:\


    #define BREAK(bn) if(!_YOU_CANNOT_USE_NAMED_BREAK_IF_YOU_ARE_OUTSIDE_##bn##_){} \
                        else goto _break_the_loop_labelled_##bn##_

Это выглядит уродливо и громоздко, потому что также позволяет избежать некоторых предупреждений, которые могут быть сгенерированы MSVC или GCC, таких как «неиспользуемая переменная», «метка без ссылки» или «предложить явные фигурные скобки». Более того, он не должен компилироваться при неправильном использовании,и в этом случае сообщение об ошибке будет понятным. Например:

    NAMED(loop1) for(int i = 0 ; i < 10; i++) {
        NAMED(loop2) for(int j = 0 ; j < i ; j++) {
            cout << i << "," << j << endl;
            if(j == 5) {
                BREAK(loop1);   // this one is okay, we exit the outer loop
            }
        }
        BREAK(loop2); // we're outside loop2, this is an error
    }

Предыдущий код не скомпилируется, а сообщение об ошибке компилятора для второгоBREAK:' _ВЫ _НЕ МОЖЕТЕ _ИСПОЛЬЗОВАТЬ _NAMED _BREAK _ЕСЛИ _ВЫ _НАХОДИТЕСЬ _ВНЕШНИМ _loop2 _`, что очень явно.

Пример

Прежде чем задать свой вопрос, я приведу два примера, иллюстрирующих (относительную )полезность этих конструкций:

breakвнешний цикл:

     NAMED(myloop) for(int i = 0 ; i < rows; i++) {
         for(int j = 0 ; j < cols ; j++) {
            if(some_condition) {
                 BREAK(myloop);
            }
         }
     }

Выход из определенной области:

NAMED(myscope1) {
    cout<< "a";
    NAMED(myscope2)
    {
        cout << "b";
        NAMED(myscope3)
        {
            cout << "c";
            BREAK(myscope2);
            cout << "d";
        }
        cout << "e";
    }
    cout <<"f";
}

Этот код печатает:abcf.

Мой вопрос

Прежде чем определить, в чем заключается мой вопрос, я должен определить, чем он не является, поскольку я не хочу, чтобы моя тема была закрыта через 10 минут.

Это не "Хорошая ли это идея?" и не "Что вы думаете?" или даже «Полезно ли это?», поскольку stackoverflow, похоже, не является местом для обсуждения. Во всяком случае, я уже знаю ответы :«Макросы — это зло», «Гото — это зло» и «Вместо этого используйте лямбда-выражения».

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

  • Может ли пользователь неправильно использовать его и при этом иметь возможность компилировать? Я пытался исправить очевидные проблемы оригинальной реализации, но C++ очень сложен и, возможно, я что-то упустил?

  • Может ли он незаметно сломать красивый код? Это моя главная забота. Может ли это мешать другим функциям C++ (обработке исключений, вызовам деструктора, другим условным операторам или чему-то еще... )?

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

РЕДАКТИРОВАТЬ :Логическая переменная теперьconst(благодаря Йенсу Густедту ).Я попытаюсь заменить ifна forпозже, чтобы проверить, может ли он удалить ложное предупреждение при таком использовании:

if(true)
     BREAK(label);

EDIT2 :Как заметил JensGustedt, объявление переменной в операторе ifне разрешено только в C (C++ ). Еще одна причина заменить его петлей.

5
задан Community 23 May 2017 в 11:59
поделиться