Оператор C++ Try-Catch не перехватывает исключение

Я программирую очень простую структуру модульного тестирования на С++, которая использует операторы try-catch для анализа переменных. В этом примере у меня есть класс под названием UnitTest с одной общедоступной и одной частной функцией-членом с названиями scalars_are_equal() и is_equal() соответственно. Пользователь должен передать str публичной функции, которая содержит строку с названием теста. Пользователь также должен передать value1 и value2 общедоступной функции, которая содержит два скалярных значения для сравнения друг с другом. Публичная функция использует оператор try и передает данные частной функции, где две переменные оцениваются, чтобы увидеть, совпадают ли они. Если значения совпадают, он возвращается к вызывающей функции, где на экран выводится сообщение, чтобы пользователь знал, что тест пройден. Если значения не равны, то приватная функция должна генерировать исключение, назначенное строке msg, а публичная функция должна перехватывать это исключение. Класс прикреплен ниже. Функции написаны в виде шаблонной функции, поэтому пользователь может выбирать для сравнения целые числа, числа с плавающей запятой и удвоения, даже если арифметика с плавающей запятой может означать, что две версии числа не совсем идентичны.

class UnitTest
{
public:
    template <class type1, class type2>
    void scalars_are_equal(std::string str, const type1 &value1, const type2 &value2);
private:
    template <class type1, class type2>
    void is_equal(const type1 &value1, const type2 &value2, std::string str);
};
// ================================================================
// ================================================================
// UnitTest PUBLIC member functions
template <class type1, class type2>
void UnitTest::scalars_are_equal(std::string str, const type1 &value1,
                             const type2 &value2)

{
    unsigned long remain;
    remain = 60 - str.length();
    try {
        is_equal(value1, value2, str);
        std::cout << str + std::string(remain, '.') +
        std::string("PASSED") << std::endl;
    }
    catch (const char* msg) {
        std::cout << msg << std::endl;
    }
}
// ----------------------------------------------------------------

template <class type1, class type2>
void UnitTest::is_equal(const type1 &value1, const type2 &value2,
                        std::string str)
{
    if (value1 != value2) {
        unsigned long remain;
        remain = 60 - str.length();
        std::string msg = str + std::string(remain, '.') + " FAILED";
        throw msg;
    }

}

В этом случае основная программа выглядит так;

#include <iostream>
#include <string>
#include <vector>
#include <array>

#include "unit_test.hpp"

int main(int argc, const char * argv[]) {
    UnitTest q;
{    // Test to see if code catches unequal scalars
    float a, b;
    a = 20.0;
    b = 30.0;
    std::string c ("Scalars_Unequal_Test");
    q.scalars_are_equal(c, a, b);
}

По непонятным мне причинам оператор catch в функции scalars_are_equal() не перехватывает функцию is_equal(). Сначала я подумал, что это может быть из-за того, что функция выдает std::string, но когда я изменил оператор catch с const char на std::string, это не имело значения. Кто-нибудь знает, почему это не ловит исключение?

Вам действительно не нужен весь этот код для показать проблему. Подсказка: std::string не const char *.

PaulMcKenzie 03.05.2019 05:22
но когда я изменил оператор catch с const char на std::string, это не имело значения -- Опубликуйте эту попытку.
PaulMcKenzie 03.05.2019 05:24

Способен воспроизвести const char *, но это ожидаемое поведение. Не удалось воспроизвести версию std::string. Изменения, которые мне пришлось внести в код для его компиляции, могли что-то изменить. Можем ли мы получить минимальный воспроизводимый пример, чтобы попробовать самостоятельно?

user4581301 03.05.2019 05:32

В какой-то несвязанной заметке: на самом деле это не то, для чего нужен try and catch. Try and catch предназначен для обнаружения и устранения ошибок, а не для передачи данных туда и обратно, что, похоже, вы и делаете.

user10957435 03.05.2019 05:52

@Chipster Я бы сказал, что создание исключения для сбоя модульного теста вполне разумно, так работают многие среды модульного тестирования.

Alan Birtles 03.05.2019 11:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
2 831
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы бросаете std::string, а не char* в UnitTest::is_equal().

std::string msg = str + std::string(remain, '.') + " FAILED";
throw msg;

Итак, вам нужно поймать строку:

catch (std::string& msg) {
    std::cout << msg << std::endl;
}

Важное примечание: не выбрасывайте std::string или подобные, а классы, производные от std::exception или вашего собственного базового класса исключений. Например:

 std::string msg = str + std::string(remain, '.') + " FAILED";
 throw std::runtime_error(msg);
 [...]
 catch (std::runtime_error& e) {
    std::cout << e.what() << std::endl;
 }

Тип вашего улова должен быть таким же, как тип, который вы выбрасываете (или родительский класс этого типа).

Ваш код по существу сводится к

try
{
  throw std::string( "test" );
}
catch ( const char* msg )
{
}

Вы выбрасываете std::string, но ловите const char*, поэтому исключение не перехватывается. Вам нужно либо бросить "test" напрямую, не оборачивая его в std::string, либо изменить улов на std::string&.

Выброс любого типа указателя приводит к проблемам с владением, кто несет ответственность за освобождение указателя? Например, в вашем коде, если вы «исправили» проблему, вызвав throw msg.c_str(), строка msg будет уничтожена до того, как будет достигнут ваш блок catch, а указатель char* будет недействительным, чтобы исправить это, вам придется динамически выделить новый массив char и скопируйте msg в него, тогда блоку catch потребуется delete массив, но он не может знать, что ему это нужно.

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

try
{
  // lots of code including calling third party libraries
}
catch ( std::exception& ex )
{
  std::cout << "uncaught exception: " << ex.what() << "\n";
}

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

Другие вопросы по теме