Простое ведение журнала отладки

Я новичок в C ++ и хотел добавить причудливый способ входа в систему. Таким образом, я создал небольшой фрагмент кода для этого, но мне интересно, как я могу улучшить свой код, особенно тот факт, что DebugColors состоит из двух файлов, и что касается использования мной шаблонов, я не думаю, что adder методы структуры Debug — лучший способ объединения строк.

Кроме того, я не уверен, следует ли мне использовать структуру, чтобы содержать методы, или я должен использовать для нее пространство имен, поскольку я хочу иметь доступ только к общедоступным методам извне структуры.

DebugColors.hpp

#ifndef DEBUG_COLORS
#define DEBUG_COLORS

#include <string>

namespace DebugColors {
    extern std::string reset;
    extern std::string red;
    extern std::string green;
    extern std::string yellow;
    extern std::string blue;
}

#endif

DebugColors.cpp

#include "DebugColors.hpp"

namespace DebugColors {
    std::string reset = "x1b[0m";
    std::string red = "x1b[31m";
    std::string green = "x1b[32m";
    std::string yellow = "x1b[33m";
    std::string blue = "x1b[34m";
}

Debug.hpp

#ifndef DEBUG_HPP
#define DEBUG_HPP

#include <string>
#include <iostream>
#include "DebugColors.hpp"

struct Debug {
    private:
        template<typename T>
        static T adder(T v) {
            return v;
        }

        template<typename T, typename ...Args>
        static T adder(T first, Args ...args) {
            return first + " " + adder<std::string>(args...);
        }

        template<typename ...T>
        static void LogMessage(T&... args) {
            std::string message = adder<std::string>(args...);
            
            std::cout << message << std::endl;
        }
    public:
    template<typename ...T>
        static void Log(T&... args) {
            LogMessage(args...);
        }

        template<typename ...T>
        static void Info(T&... args) {
            LogMessage(DebugColors::blue, "[INFO]", args..., DebugColors::reset);
        }

        template<typename ...T>
        static void Warning(T&... args) {
            LogMessage(DebugColors::yellow, "[WARN]", args..., DebugColors::reset);
        }
        
        template<typename ...T>
        static void Error(T&... args) {
            LogMessage(DebugColors::red, "[ERROR]", args..., DebugColors::reset);
        }

        template<typename ...T>
        static void Success(T&... args) {
            LogMessage(DebugColors::green, "[SUCCESS]", args..., DebugColors::reset);
        }
};

#endif
```

1 ответ
1

С использованием namespaces

Мы можем поместить все в namespace что позволило бы нам использовать using.

Рассмотрим этот пример,

using Debug::Log; //Error if it's a static member function
                  // Error msg: error: ‘Debug’ is not a namespace or unscoped enum

namespace Debug{
   Type Func(){ ... };
}

using Debug::Func; // No error

Func(); // Calls `Debug::Func`

С, Log, Info, Error, Warning а также Success являются просто автономными служебными функциями. Не вижу смысла ставить их в класс.

Мы можем поместить частные функции в другой namespace сказать impl, пространство имен, которое будет содержать частные функции Debug класс.

namespace impl{
  std::string add(...) {...}
  void LogMessage(...) {...}
}

namespace Debug{
  void Log(...) { impl::LogMessage(...) }
  void Info(...) { impl::LogMessage(...) }
  void Error(...) { impl::LogMessage(...) }
}

Использовать std::to_string печатать intпесок floats

Создать to_str функция, которая будет вызывать std::string. Почему это необходимо, объясняется в нижеследующих параграфах.

namespace impl {

template<typename T>
std::string to_str(const T& val){
    return std::to_string(val);
}

std::string to_str(const std::string& val){
    return val;
}

std::string to_str(const char * str){
    return std::string(str);
}

} // namespace impl

Свернуть выражение, начиная с C ++ 17

Мы можем использовать выражение свертки при работе с вариативными функциями. Мы можем переписать adder как таковой.

namespace impl{

template <typename... Args>
std::string adder(Args... args) {
  return ((to_str(args) + " ") + ...);
}

}

ссылка на пересылку

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

namespace Debug{

template <typename... T>
void Log(T&&... args) {
  impl::LogMessage(args...);
}
// Similarly for other functions too.
}

Полный код

Debug.hpp

#ifndef DEBUG_HPP
#define DEBUG_HPP

#include <iostream>
#include <string>

#include "DebugColors.h"

namespace impl {

template<typename T>
std::string to_str(const T& val){
    return std::to_string(val);
}

std::string to_str(const std::string& val){
    return val;
}

std::string to_str(const char * str){
    return std::string(str);
}

template <typename... Args>
std::string adder(Args&&... args) {
  return ((to_str(args) + " ") + ...);
}

template <typename... T>
void LogMessage(T&&... args) {
  std::string message = adder(args...);

  std::cout << message << std::endl;
}
}  // namespace impl

namespace Debug {
template <typename... T>
void Log(T&&... args) {
  impl::LogMessage(args...);
}

template <typename... T>
void Info(T&&... args) {
  impl::LogMessage(DebugColors::blue, "[INFO]", args..., DebugColors::reset);
}

template <typename... T>
void Warning(T&&... args) {
  impl::LogMessage(DebugColors::yellow, "[WARN]", args..., DebugColors::reset);
}

template <typename... T>
void Error(T&&... args) {
  impl::LogMessage(DebugColors::red, "[ERROR]", args..., DebugColors::reset);
}

template <typename... T>
void Success(T&&... args) {
  impl::LogMessage(DebugColors::green, "[SUCCESS]", args...,
                   DebugColors::reset);
}

}  // namespace Debug

#endif

test.cpp

include "Debug.hpp"

using Debug::Error;
int main(){
  int t = 10;
  Error("Danger", 20, t, 3.14);
}

Выход:

введите описание изображения здесь


Вы можете использовать clang-format для форматирования файлов кода.

Команда терминала:

clang-format -style=Google -i files

  • Хороший обзор! Что вы думаете об упоминании std::string_view для цветовых кодов?

    — Эдвард

  • @ Эдвард, я бы тоже это посоветовал. Но этот переписанный код без необходимости преобразует его в string каждый раз. Он также преобразует const char*.

    — JDłuosz

  • @ Ch3steR вместо to_str назови это wrap или что-то, что не подразумевает определенный тип возвращаемого значения. Использовать string_view в качестве прибыли для const char* а также string_view сам, а также string поэтому вам не нужно его копировать. Но держи string return для чисел и тому подобного, где он выделяет память как часть преобразования.

    — JDłuosz

  • @ Эдвард Спасибо. да string_view гораздо лучше.

    — Ч3СТЕР

  • @ JDługosz Да, согласен, to_str без надобности создает копии. Я не рядом со своим ноутбуком. Можете ли вы добавить ответ, поскольку это очень хорошее предложение?

    — Ч3СТЕР



Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *