Как печатать значения enum в C++23

Как печатать значения enum в C++23 #

Допустим, у нас есть enum или enum class. Как обеспечить печать его значений в целях диагностики и отладки?

Задача #

Допустим, в заголовочном файле объявлен enum class ast::BinaryOperation с такими значениями:

#pragma once

namespace ast
{
enum class BinaryOperation
{
    Add,
    Subtract,
    Multiply,
    Divide,
};
} // namespace ast

Нужно уметь выводить человеко-читаемое значение BinaryOperation в std::ostream, с помощью std::format и просто конкатенировать с какими-либо строками.

Предупреждение #

Возможно, в вашем проекте уже есть универсальный способ печати значений enum или сопоставления enum со значением:

  • такое решение сложно написать с нуля;
  • но если оно уже есть — используйте.

Решение #

Перепишем заголовочный файл, добавив следующее:

  1. заголовочные файлы
  2. функцию binaryOperationName
  3. оператор вывода в std::ostream
  4. специализацию шаблона std::formatter

Новая версия:

#pragma once

#include <format>
#include <iosfwd>
#include <string_view>

namespace ast
{
enum class BinaryOperation
{
    Add,
    Subtract,
    Multiply,
    Divide,
};

std::string_view binaryOperationName(BinaryOperation operation);

// Поддержка вывода в std::ostream.
std::ostream& operator<<(std::ostream& os, BinaryOperation operation);

} // namespace ast

// Поддержка std::format(...).
template<>
struct std::formatter<ast::BinaryOperation> : std::formatter<string_view>
{
    auto format(const ast::BinaryOperation& value, std::format_context& ctx) const
    {
        std::string_view name = ast::binaryOperationName(value);
        return std::formatter<std::string_view>::format(name, ctx);
    }
};

Затем добавим файл BinaryOperation.cpp:

#include "BinaryOperation.h"

namespace ast
{
std::string_view binaryOperationName(BinaryOperation operation)
{
    switch (operation)
    {
    case BinaryOperation::Add:
        return "Add";
    case BinaryOperation::Subtract:
        return "Subtract";
    case BinaryOperation::Multiply:
        return "Multiply";
    case BinaryOperation::Divide:
        return "Divide";
    default:
        return "!!Unknown!!";
    }
}

std::ostream& operator<<(std::ostream& os, BinaryOperation type)
{
    os << binaryOperationName(type);
    return os;
}
}

Особенности #

  1. Функция binaryOperationName позволяет поддержать сценарии, которые вы не могли предусмотреть при написании BinaryOperation
  2. При неожиданном значении выводится "!!Unknown!!" вместо выброса исключения
  3. Можно сделать всё header-only, но я лично не сторонник такого подхода

Сайт atdd.ru — блог разработчика.