Introducción a easylog, una biblioteca de registros de alto rendimiento basada en c++17

Hay muchas opciones para usar bibliotecas de registro en C++, como spdlog, g3log, log4cxx, log4cplus, log4qt, etc. Todos son fáciles de usar y potentes, pero algunos son un poco pesados ​​en términos de cantidad de código fuente. Aquí presentamos easylog, una biblioteca de registros liviana con excelente rendimiento, de la biblioteca Yalanting de Ali. Solo unos pocos archivos simples, solo incluya el archivo de encabezado cuando lo use.

Introducción a easylog

easylog, una biblioteca de registros de C++ liviana y de alto rendimiento de código abierto de Ali, requiere un soporte mínimo del compilador para C++17. Pertenece a una función integrada en la biblioteca Yalan Pavilion de Alibaba, con una pequeña cantidad de código, fácil de usar y un rendimiento potente. Utiliza algunas características nuevas superiores a c++ 17, como la optimización del tiempo de compilación de constexpr, la clase de vista de cadena std::string_view y las bibliotecas de terceros ConcurrentQueue (cola sin bloqueo segura para subprocesos), jkj::dragonbox (cola flotante eficiente -número de punto a biblioteca de conversión de cadenas), metaprogramación de cadenas efvalue::meta_string y, por lo tanto, tiene un alto rendimiento.

GitHub - purecpp-org/easylog: una biblioteca de registro de c++20

implementación de registro fácil

La idea de implementación de easylog es relativamente simple y clara. Es para escribir el registro en la cola y no para operar el archivo directamente. Inicie un hilo para tomar datos de la cola y escribirlos en el archivo. Aunque la cantidad de código es pequeña y la idea de implementación es simple, se utilizan muchas características nuevas de optimización del rendimiento de C ++, de las cuales vale la pena aprender y aprender.

Cola concurrente

ConcurrentQueue es una estructura de datos de cola segura para subprocesos que permite que varios subprocesos operen en la cola al mismo tiempo sin mecanismos de sincronización adicionales. Es una de las estructuras de datos más utilizadas en la programación concurrente.

moodycamel::ConcurrentQueue Una cola sin bloqueos multiproductor y multiconsumidor implementada en C++11.

Dirección del almacén:

https://github.com/cameron314/concurrentqueue

Las ventajas son las siguientes:

1. Seguridad de subprocesos: ConcurrentQueue proporciona un mecanismo de seguridad de subprocesos incorporado, que puede realizar operaciones de puesta en cola y retirada de cola en varios subprocesos al mismo tiempo, sin agregar manualmente mecanismos de sincronización adicionales (como mutex o semáforos) para proteger los recursos compartidos. Esto simplifica las complejidades de la programación concurrente.

2. Rendimiento eficiente: ConcurrentQueue proporciona un mejor rendimiento en un entorno concurrente. Utiliza algunos algoritmos y estructuras de datos eficientes, como colas sin bloqueos o bloqueos detallados, para reducir la competencia y mejorar el rendimiento de la concurrencia.

3. Baja latencia: dado que ConcurrentQueue está diseñado para admitir escenarios de alta concurrencia y baja latencia, generalmente tiene una latencia operativa baja. Esto es útil para aplicaciones que necesitan responder rápidamente y manejar una gran cantidad de solicitudes simultáneas.

4. Escalabilidad: ConcurrentQueue puede expandirse fácilmente a más subprocesos cuando sea necesario sin cuellos de botella en el rendimiento. Es adecuado para aplicaciones de alta concurrencia y alto rendimiento y se puede escalar horizontalmente según la demanda.

ConcurrentQueue es una estructura de datos de cola conveniente, eficiente y segura para subprocesos, adecuada para escenarios de programación concurrente y puede proporcionar un mejor rendimiento y escalabilidad.

biblioteca-dragonbox

jkj::dragonbox es una biblioteca de C++ para conversiones eficientes de flotante a cadena. Proporciona una forma rápida y precisa de representar números de coma flotante como cadenas decimales, adecuada para diversos escenarios de aplicaciones, como formato de números, registro, etc.

Dirección del almacén:

https://github.com/jk-jeon/dragonbox

Las principales características y usos de la biblioteca jkj::dragonbox:

1. Rendimiento eficiente: jkj::dragonbox utiliza algunos algoritmos y técnicas eficientes para lograr una conversión rápida de números de punto flotante a cadenas. Es más rápido que las funciones de conversión de la biblioteca estándar en la mayoría de los casos y tiene un rendimiento predecible.

2. Precisión: la biblioteca jkj::dragonbox proporciona resultados de conversión precisos, puede retener todos los dígitos significativos de los números de punto flotante y puede manejar correctamente los errores de redondeo al redondear.

3. Portabilidad: La biblioteca jkj::dragonbox es una biblioteca C++ multiplataforma que se puede utilizar en varios sistemas operativos y compiladores. 4. Facilidad de uso: La interfaz de la biblioteca jkj::dragonbox es simple y fácil de usar, solo necesita incluir el archivo de encabezado correspondiente y llamar a la función de conversión correspondiente para completar la conversión de números de punto flotante a cadenas.

La biblioteca jkj::dragonbox proporciona una solución de conversión de punto flotante a cadena eficiente, precisa y portátil, adecuada para escenarios de aplicaciones que requieren alto rendimiento y precisión.

característica constexpr

constexpr es una palabra clave introducida por C ++ 11, que se utiliza para declarar una constante de tiempo de compilación (constante de tiempo de compilación). Puede evaluarse en tiempo de compilación y optimizarse en tiempo de compilación, brindando la capacidad de realizar cálculos e inicialización en tiempo de compilación.

1. Expresión constante: constexpr se puede utilizar para declarar una expresión constante, es decir, una expresión cuyo valor se puede determinar en tiempo de compilación. Estas expresiones se pueden evaluar en tiempo de compilación y no es necesario evaluarlas en tiempo de ejecución.

2. Optimización en tiempo de compilación: las expresiones constantes declaradas mediante constexpr se pueden optimizar en tiempo de compilación para mejorar el rendimiento del programa. El compilador puede calcular el resultado de una expresión constexpr en tiempo de compilación y reemplazarlo directamente con el valor resultante sin tener que realizar el cálculo en tiempo de ejecución.

3. Comprobación de tipos: constexpr también se puede utilizar para declarar variables miembro de funciones, constructores, funciones miembro y clases. Estas declaraciones se pueden verificar en tiempo de compilación para garantizar que cumplan con los requisitos de constexpr.

4. Tamaño de la matriz: constexpr se puede utilizar para declarar el tamaño de la matriz, es decir, el tamaño de la matriz se determina en el momento de la compilación. De esta manera, la verificación estática se puede realizar en tiempo de compilación para evitar problemas como matrices fuera de límites. El uso de constexpr puede mejorar el rendimiento y la legibilidad de su código, al tiempo que permite más cálculos y optimizaciones en el momento de la compilación. La característica de su evaluación en tiempo de compilación permite que el cálculo de algunas constantes se complete en tiempo de compilación sin la necesidad de realizar cálculos en tiempo de ejecución, mejorando así la eficiencia del programa.

característica meta_string

GET_STRING definición de macro

Se utiliza para generar un prefijo de cadena que contiene información sobre el nombre de archivo y el número de línea en el momento de la compilación. Su función es agregar un prefijo que incluya el nombre del archivo y el número de línea a cada mensaje de registro en escenarios como la salida del registro, para localizar el origen del mensaje de registro.

#define TO_STR(s) #s

#define GET_STRING(filename, line)                              \
  [] {                                                          \
    constexpr auto path = refvalue::meta_string{filename};      \
    constexpr size_t pos =                                      \
        path.rfind(std::filesystem::path::preferred_separator); \
    constexpr auto name = path.substr<pos + 1>();               \
    constexpr auto prefix = name + ":" + TO_STR(line);          \
    return "[" + prefix + "] ";                                 \
  }()

De hecho, puede considerarse como una herramienta de metaprogramación que genera cadenas literales en tiempo de compilación. Proporciona la capacidad de crear cadenas en tiempo de compilación y realizar operaciones y cálculos relacionados con cadenas en tiempo de compilación. Define una estructura de plantilla para procesar cadenas durante la compilación y proporciona algunas funciones de manipulación de cadenas.

#pragma once

#include <algorithm>
#include <array>
#include <cstddef>
#if __has_include(<span>)
#include <compare>
#include <concepts>
#include <span>
#endif
#include <string_view>
#include <utility>

namespace refvalue {
template <std::size_t N>
struct meta_string {
  std::array<char, N + 1> elements_;

  constexpr meta_string() noexcept : elements_{} {}

  constexpr meta_string(const char (&data)[N + 1]) noexcept {
    for (size_t i = 0; i < N + 1; i++) elements_[i] = data[i];
  }

#if __has_include(<span>)
  template <std::size_t... Ns>
  constexpr meta_string(std::span<const char, Ns>... data) noexcept
      : elements_{} {
    auto iter = elements_.begin();

    ((iter = std::copy(data.begin(), data.end(), iter)), ...);
  }
#endif

  template <std::size_t... Ns>
  constexpr meta_string(const meta_string<Ns>&... data) noexcept : elements_{} {
    auto iter = elements_.begin();

    ((iter = std::copy(data.begin(), data.end(), iter)), ...);
  }

#if __has_include(<span>)
  template <std::same_as<char>... Ts>
  constexpr meta_string(Ts... chars) noexcept requires(sizeof...(Ts) == N)
      : elements_{chars...} {}
#endif

  constexpr char& operator[](std::size_t index) noexcept {
    return elements_[index];
  }

  constexpr const char& operator[](std::size_t index) const noexcept {
    return elements_[index];
  }

  constexpr operator std::string_view() const noexcept {
    return std::string_view{elements_.data(), size()};
  }

  constexpr bool empty() const noexcept { return size() == 0; }

  constexpr std::size_t size() const noexcept { return N; }

  constexpr char& front() noexcept { return elements_.front(); }

  constexpr const char& front() const noexcept { return elements_.front(); }

  constexpr char& back() noexcept { return elements_[size() - 1]; }

  constexpr const char& back() const noexcept { return elements_[size() - 1]; }

  constexpr auto begin() noexcept { return elements_.begin(); }

  constexpr auto begin() const noexcept { return elements_.begin(); }

  constexpr auto end() noexcept { return elements_.begin() + size(); }

  constexpr auto end() const noexcept { return elements_.begin() + size(); }

  constexpr char* data() noexcept { return elements_.data(); }

  constexpr const char* data() const noexcept { return elements_.data(); };

  constexpr const char* c_str() const noexcept { return elements_.data(); }

  constexpr bool contains(char c) const noexcept {
    return std::find(begin(), end(), c) != end();
  }

  constexpr bool contains(std::string_view str) const noexcept {
    return str.size() <= size()
               ? std::search(begin(), end(), str.begin(), str.end()) != end()
               : false;
  }

  static constexpr size_t substr_len(size_t pos, size_t count) {
    if (pos >= N) {
      return 0;
    }
    else if (count == std::string_view::npos || pos + count > N) {
      return N - pos;
    }
    else {
      return count;
    }
  }

  template <size_t pos, size_t count = std::string_view::npos>
  constexpr meta_string<substr_len(pos, count)> substr() const noexcept {
    constexpr size_t n = substr_len(pos, count);

    meta_string<n> result;
    for (int i = 0; i < n; ++i) {
      result[i] = elements_[pos + i];
    }
    return result;
  }

  constexpr size_t rfind(char c) const noexcept {
    return std::string_view(*this).rfind(c);
  }

  constexpr size_t find(char c) const noexcept {
    return std::string_view(*this).find(c);
  }
};

template <std::size_t N>
meta_string(const char (&)[N]) -> meta_string<N - 1>;

#if __has_include(<span>)
template <std::size_t... Ns>
meta_string(std::span<const char, Ns>...) -> meta_string<(Ns + ...)>;
#endif

template <std::size_t... Ns>
meta_string(const meta_string<Ns>&...) -> meta_string<(Ns + ...)>;

#if __has_include(<span>)
template <std::same_as<char>... Ts>
meta_string(Ts...) -> meta_string<sizeof...(Ts)>;
#endif

#if __has_include(<span>)
template <std::size_t M, std::size_t N>
constexpr auto operator<=>(const meta_string<M>& left,
                           const meta_string<N>& right) noexcept {
  return static_cast<std::string_view>(left).compare(
             static_cast<std::string_view>(right)) <=> 0;
}
#endif

template <std::size_t M, std::size_t N>
constexpr bool operator==(const meta_string<M>& left,
                          const meta_string<N>& right) noexcept {
  return static_cast<std::string_view>(left) ==
         static_cast<std::string_view>(right);
}

template <std::size_t M, std::size_t N>
constexpr bool operator==(const meta_string<M>& left,
                          const char (&right)[N]) noexcept {
  return static_cast<std::string_view>(left) ==
         static_cast<std::string_view>(meta_string{right});
}

template <std::size_t M, std::size_t N>
constexpr auto operator+(const meta_string<M>& left,
                         const meta_string<N>& right) noexcept {
  return meta_string{left, right};
}

template <std::size_t M, std::size_t N>
constexpr auto operator+(const meta_string<M>& left,
                         const char (&right)[N]) noexcept {
  meta_string<M + N - 1> s;
  for (size_t i = 0; i < M; ++i) s[i] = left[i];
  for (size_t i = 0; i < N; ++i) s[M + i] = right[i];
  return s;
}

template <std::size_t M, std::size_t N>
constexpr auto operator+(const char (&left)[M],
                         const meta_string<N>& right) noexcept {
  meta_string<M + N - 1> s;
  for (size_t i = 0; i < M - 1; ++i) s[i] = left[i];
  for (size_t i = 0; i < N; ++i) s[M + i - 1] = right[i];
  return s;
}

#if __has_include(<span>)
template <meta_string S, meta_string Delim>
struct split_of {
  static constexpr auto value = [] {
    constexpr std::string_view view{S};
    constexpr auto group_count = std::count_if(S.begin(), S.end(),
                                               [](char c) {
                                                 return Delim.contains(c);
                                               }) +
                                 1;
    std::array<std::string_view, group_count> result{};

    auto iter = result.begin();

    for (std::size_t start_index = 0, end_index = view.find_first_of(Delim);;
         start_index = end_index + 1,
                     end_index = view.find_first_of(Delim, start_index)) {
      *(iter++) = view.substr(start_index, end_index - start_index);

      if (end_index == std::string_view::npos) {
        break;
      }
    }

    return result;
  }();
};

template <meta_string S, meta_string Delim>
inline constexpr auto&& split_of_v = split_of<S, Delim>::value;

template <meta_string S, meta_string Delim>
struct split {
  static constexpr std::string_view view{S};
  static constexpr auto value = [] {
    constexpr auto group_count = [] {
      std::size_t count{};
      std::size_t index{};

      while ((index = view.find(Delim, index)) != std::string_view::npos) {
        count++;
        index += Delim.size();
      }

      return count + 1;
    }();
    std::array<std::string_view, group_count> result{};

    auto iter = result.begin();

    for (std::size_t start_index = 0, end_index = view.find(Delim);;
         start_index = end_index + Delim.size(),
                     end_index = view.find(Delim, start_index)) {
      *(iter++) = view.substr(start_index, end_index - start_index);

      if (end_index == std::string_view::npos) {
        break;
      }
    }

    return result;
  }();
};

template <meta_string S, meta_string Delim>
inline constexpr auto&& split_v = split<S, Delim>::value;

template <meta_string S, char C>
struct remove_char {
  static constexpr auto value = [] {
    struct removal_metadata {
      decltype(S) result;
      std::size_t actual_size;
    };

    constexpr auto metadata = [] {
      auto result = S;
      auto removal_end = std::remove(result.begin(), result.end(), C);

      return removal_metadata{
          .result{std::move(result)},
          .actual_size{static_cast<std::size_t>(removal_end - result.begin())}};
    }();

    meta_string<metadata.actual_size> result;

    std::copy(metadata.result.begin(),
              metadata.result.begin() + metadata.actual_size, result.begin());

    return result;
  }();
};

template <meta_string S, char C>
inline constexpr auto&& remove_char_v = remove_char<S, C>::value;

template <meta_string S, meta_string Removal>
struct remove {
  static constexpr auto groups = split_v<S, Removal>;
  static constexpr auto value = [] {
    return []<std::size_t... Is>(std::index_sequence<Is...>) {
      return meta_string{std::span<const char, groups[Is].size()>{
          groups[Is].data(), groups[Is].size()}...};
    }
    (std::make_index_sequence<groups.size()>{});
  }();
};

template <meta_string S, meta_string Removal>
inline constexpr auto&& remove_v = remove<S, Removal>::value;
#endif
}  // namespace refvalue

Las funciones principales de refvalue::meta_string son las siguientes:

1. Operaciones de cadenas en tiempo de compilación: refvalue::meta_string permite operaciones en cadenas en tiempo de compilación, como concatenación, interceptación, comparación, etc. Esto permite que los cálculos y el procesamiento relacionados con cadenas se realicen en tiempo de compilación en lugar de en tiempo de ejecución.

2. Generación de código: refvalue::meta_string se puede utilizar para generar código, especialmente código relacionado con cadenas. Al generar cadenas literales en tiempo de compilación, puede incluir cadenas como parte de su código y manipularlas y procesarlas durante la generación del código.

3. Procesamiento de metadatos: refvalue::meta_string se puede utilizar para procesar metadatos de tipo cadena, como nombre de clase, nombre de función, etc. Al generar cadenas literales en tiempo de compilación, estas cadenas se pueden usar en metaprogramación como nombres para tipos e identificadores.

meta_stringestá diseñado para realizar manipulación de cadenas durante el tiempo de compilación para mejorar el rendimiento. Dado que la operación ocurre en el momento de la compilación, puede evitar la sobrecarga de la operación de cadena en el tiempo de ejecución, mejorando así la eficiencia de ejecución del programa. Por ejemplo, usando meta_string, puede concatenar cadenas, extraer subcadenas, eliminar caracteres, etc., en tiempo de compilación en lugar de en tiempo de ejecución. Esto hace que el código sea más flexible y eficiente.

En resumen, refvalue::meta_string es una herramienta de metaprogramación para generar cadenas literales en tiempo de compilación y realizar operaciones y procesamiento relacionados con cadenas. Se puede utilizar para generar código, procesar metadatos y realizar cálculos y operaciones de cadenas en tiempo de compilación, proporcionando una capacidad para el procesamiento de cadenas en tiempo de compilación.

Cabe señalar que algunas funciones de este código pueden requerir compatibilidad con C++ 20 o posterior, como funciones como std::span y std::same_as. Por lo tanto, al utilizar estas funciones, debe asegurarse de que su compilador las admita.

Ejemplo de uso de meta_string

#include <iostream>
#include "meta_string.hpp"

int main() {
    using namespace refvalue;

    constexpr auto str = meta_string("hello world");

    static_assert(str.size() == 6, "字符串长度不正确");

    std::cout << "字符串: " << str << std::endl;
    std::cout << "字符串长度: " << str.size() << std::endl;

    constexpr auto subStr = str.substr<3, 2>();

    std::cout << "子串: " << subStr << std::endl;
    std::cout << "子串长度: " << subStr.size() << std::endl;

    constexpr auto concatStr = str + meta_string(" 欢迎!");

    std::cout << "拼接后的字符串: " << concatStr << std::endl;

    constexpr auto removedStr = remove_v<concatStr, meta_string("迎")>;

    std::cout << "移除后的字符串: " << removedStr << std::endl;

    return 0;
}

En el ejemplo anterior, se crea e inicializa un meta_stringobjeto str, luego mostramos algunas meta_stringoperaciones en el archivo . Utilice std::cout para imprimir la cadena original y su longitud, utilice substrla función para extraer una subcadena, especifique la posición inicial y la longitud e imprima la subcadena y su longitud. Concatenamos con otro usando +el operador y almacenamos el resultado en . Finalmente, la subcadena "bienvenida" se elimina con la función y el resultado se almacena en . Las palabras clave se utilizan para garantizar que estas operaciones se evalúen en el momento de la compilación.  strmeta_stringconcatStrremove_vconcatStrremovedStrconstexpr

meta_stringPermite la manipulación de cadenas en tiempo de compilación, no en tiempo de ejecución. Esto significa que el resultado del procesamiento de cadenas se determina antes de ejecutar el programa, lo que puede mejorar el rendimiento y la eficiencia del programa. Dado que el procesamiento de cadenas se produce en tiempo de compilación en lugar de en tiempo de ejecución, se evita la sobrecarga de la manipulación de cadenas durante la ejecución del programa, lo que reduce el cálculo en tiempo de ejecución y el consumo de memoria. Esta es también la razón por la que se usa en easylog meta_string . Para algunas escenas con caracteres de prefijo de formato de registro fijo, como la fecha y el número de línea de código, su uso puede mejorar significativamente el rendimiento.

mecanismo de rasgos de tipo

El concepto de rasgos de tipo, traducción literal, es extracción de tipos. Según el nombre, también se puede adivinar que se usa para obtener el tipo. Antes de C ++ 11, stl ya usaba tecnologías relacionadas. Por ejemplo, los iteradores usan tipos relacionados para obtener. "Análisis de código fuente STL" tiene una introducción detallada. Si estás interesado, puedes ir a echar un vistazo. C++ 11 incluso introdujo un archivo de encabezado especial <type_traits> para hacer cosas relacionadas con los rasgos de tipo.

Entendiendo de manera simple, los rasgos de tipo en C++ son un mecanismo para consultar y manipular información de tipo en tiempo de compilación. Nos permiten obtener propiedades y características de los tipos en tiempo de compilación y programar en función de ellos. Los rasgos de tipo pueden ayudarnos a determinar si un tipo tiene ciertas características, como si es un puntero, una referencia, una matriz, etc., si es un calificador constante o volátil, o si puede realizar operaciones como copiar construcción o mover construcción. . A través de estos rasgos podemos realizar diferentes ramas lógicas y de procesamiento según las características del tipo en tiempo de compilación.

En pocas palabras, los rasgos de tipo son una herramienta para consultar y manipular atributos de tipo en tiempo de compilación, que pueden ayudarnos a programar de acuerdo con las características de los tipos y mejorar la confiabilidad y flexibilidad del código.

template <typename Return>
struct function_traits<Return (*)() noexcept> {
  using parameters_type = void;
  using return_type = Return;
};

En el fragmento de código proporcionado anteriormente, function_traitsse define una especialización de la plantilla para un puntero de función sin parámetros y un especificador noexcept. Return (*)() noexceptEsta especialización se activa cuando un parámetro de plantilla coincide con el patrón. En la especialización se definen dos tipos de alias: parameters_typey return_type. En este caso, parameters_typese establece en void, lo que indica que la función no tiene parámetros, y return_typese establece en un parámetro de plantilla Return, que indica el tipo de retorno de la función. Este código es útil cuando desea extraer o manipular información sobre el tipo de una función, como extraer el tipo de retorno o verificar si una función tiene una firma específica. Los rasgos de tipo como function_traitseste se pueden utilizar en metaprogramación de plantillas o en escenarios de programación genérica donde se requiere información de tipo en tiempo de compilación. 

Dé un ejemplo de uso simple para profundizar la comprensión:

#include <iostream>
#include <type_traits>

// 类型特性,用于检查一个类型是否为指针
template <typename T>
struct is_pointer {
    static constexpr bool value = false;
};

template <typename T>
struct is_pointer<T*> {
    static constexpr bool value = true;
};

// 使用类型特性
int main() {
    std::cout << std::boolalpha;
    std::cout << "int* 是指针吗?" << is_pointer<int*>::value << std::endl;
    std::cout << "float* 是指针吗?" << is_pointer<float*>::value << std::endl;
    std::cout << "double 是指针吗?" << is_pointer<double>::value << std::endl;

    return 0;
}

En este ejemplo, se define un is_pointerrasgo de tipo denominado que comprueba si un tipo determinado es un puntero. Este atributo se define como una plantilla de estructura que contiene una variable miembro estática valueestablecida en false. Luego is_pointer<T*>se proporciona una especialización parcial donde T*denota un tipo de puntero. En esta especialización parcial, valueestablezca en true. mainUtilice el atributo de tipo en la función is_pointerpara comprobar si los diferentes tipos son punteros. Utilice std::cout para imprimir el resultado. Para int*y float*, la salida será trueporque son tipos de puntero, y para double, falseporque no es un tipo de puntero. Este ejemplo muestra cómo utilizar rasgos de tipo para realizar la verificación de tipos en tiempo de compilación y proporcionar información sobre los tipos en tiempo de compilación. 

std::string_view

std::string_view se usa ampliamente en easylog.

std::string_view es una clase de vista de cadena ligera introducida en C++17.

ventaja

1. Abstracción sin gastos generales: std::string_view no posee ningún dato, es solo una vista de los datos de cadena existentes. Esto significa que se puede construir y transmitir de manera muy eficiente, sin la sobrecarga de copiar datos.

2. Interoperar con std::string: std::string_view se puede construir convenientemente a partir de std::string y viceversa. Esto lo hace ideal como argumento de función que puede aceptar std::string o std::string_view.

3. Seguridad: en comparación con las cadenas de estilo C, std::string_view tiene seguridad de tipos y puede evitar posibles errores de desbordamiento del búfer.

4. Flexibilidad: std::string_view puede apuntar a cualquier secuencia continua de caracteres, sin limitarse a std::string. Esto lo hace muy versátil y flexible.

Escena aplicable

1. Cuando necesita una abstracción de cadena, pero no necesita poseer los datos de cadena subyacentes. Por ejemplo, en un parámetro de función, es posible que solo necesite leer datos de cadena, pero no poseerlos ni modificarlos.

2. Cuando se requiere una vista de cadena, puede apuntar a diferentes tipos de cadena (como std::string, char*, const char*, etc.). 3. Cuando necesita construir y pasar datos de cadena de manera eficiente para evitar sobrecargas de copia innecesarias.

4. Cuando se requiere una abstracción de cadena con seguridad de tipos, puede reemplazar las cadenas de estilo C.

5. Cuando se requiere una vista de cadena que pueda interoperar con std::string, para admitir funciones que acepten std::string o std::string_view.

En resumen, std::string_view es una abstracción de cadenas liviana que manipula eficientemente datos de cadenas y proporciona seguridad de tipos e interoperabilidad con std::string. Es excelente como argumento de función o cuando necesita una vista de una cadena sin poseer los datos subyacentes.

marco de prueba unitaria doctest

El marco de pruebas unitarias de doctest se utiliza en easylog. Doctest es un marco de pruebas unitarias para C ++. En comparación con Google Test (gtest), sus principales ventajas son:

1. Facilidad de uso: la API de doctest es muy simple y solo se necesitan unas pocas definiciones de macro y comentarios para escribir casos de prueba. Por el contrario, la API de gtest es un poco más compleja y requiere más funciones y clases para definir las pruebas.

2. No es necesario vincular bibliotecas: doctest es una biblioteca de archivos de encabezado, no es necesario vincular ningún archivo de biblioteca. gtest necesita vincular dos archivos de biblioteca libgtest.a y libgtest_main.a.

3. Subpruebas de soporte: doctest admite la organización de casos de prueba en subpruebas jerárquicas, lo que hace que las pruebas sean más estructuradas y legibles. Actualmente, gtest no admite subpruebas.

4. Admite una variedad de afirmaciones: doctest proporciona una variedad de macros de afirmaciones para verificar las condiciones de la prueba, incluidas afirmaciones de igualdad, afirmaciones de verdad, afirmaciones de excepción, etc. gtest también proporciona una variedad de afirmaciones, pero un poco menos.

5. Admite etiquetado e ignoración de casos de prueba: doctest puede agregar etiquetas a los casos de prueba y luego elegir ejecutar solo casos de prueba con una determinada etiqueta. También admite ignorar casos de prueba específicos. gtest no admite estas dos funciones.

6. Admite múltiples informes de prueba: doctest puede generar informes de prueba en múltiples formatos, incluidos XML, JSON, JUnit, etc. gtest solo admite informes en formato de prueba de Google.

7. Admite orden aleatorio de casos de prueba: doctest puede cambiar aleatoriamente el orden de ejecución de los casos de prueba, lo cual es útil al detectar errores que dependen del orden de los casos de prueba. gtest no admite el orden de prueba aleatorio.

En general, doctest es un marco de prueba unitario de C++ simple, fácil de usar y más potente. En comparación con gtest, tiene más características y ventajas. Es fácil de usar y adecuado para pruebas unitarias de algunos proyectos de C++.

uso fácil de registro

easylog es relativamente sencillo de usar, puede consultar directamente los ejemplos.

  std::string filename = "easylog.txt";
  std::filesystem::remove(filename);
  easylog::init_log(Severity::DEBUG, filename, true, 5000, 1, true);

  ELOG_INFO << 42 << " " << 4.5 << 'a' << Severity::DEBUG;

  ELOGV(INFO, "test");
  ELOGV(INFO, "it is a long string test %d %s", 2, "ok");

  int len = 42;
  ELOGV(INFO, "rpc header data_len: %d, buf sz: %lu", len, 20);

  ELOG(INFO) << "test log";
  easylog::flush();
  ELOG_INFO << "hello "
            << "easylog";

  ELOGI << "same";

  ELOG_DEBUG << "debug log";
  ELOGD << "debug log";

soporte c ++ 17

Tenga en cuenta que el almacén original no admite c++17 de forma predeterminada y el requisito mínimo es c++20. Pero se pueden modificar varios códigos para admitir c++ 17.

Las siguientes definiciones están habilitadas en type_traits.h.

namespace std {
template <class T>
struct remove_cvref {
  typedef std::remove_cv_t<std::remove_reference_t<T>> type;
};
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
}  // namespace std

Elimine los archivos de encabezado y definiciones relacionados con el intervalo de meta-string.h. Porque <span> es una característica nueva de c++20.

otros recursos

Cola eficiente sin bloqueos en C++ moodycamel::ConcurrentQueue - Se busca programador

https://github.com/jk-jeon/dragonbox

espejos / cameron314 / cola concurrente · GitCode

espejos / alibaba / yalantinglibs · GitCode

https://github.com/KjellKod/g3log

GitHub - purecpp-org/easylog: una biblioteca de registro de c++20

[herramienta de registro] g3log_1_Introduction_Bubble spit bubble ah's blog-CSDN blog

https://github.com/purecpp-org/easylog/tree/da2ed3a8e74b29a73faa67896dac02e1b7584551

Esas cosas en C++ - std::string_view y std::span bzdww

Introducción y aplicación de la tecnología de programación de extracción (rasgos)_programación de rasgos_¡Hola, C++! Blog-Blog de CSDN

Soporte de tipo C ++ 11 blog de traces_type_traits_wxj1992 - Blog de CSDN

Supongo que te gusta

Origin blog.csdn.net/qq8864/article/details/132235637
Recomendado
Clasificación