

A biblioteca de asserção C ++ mais engenhosa
Filosofia da Biblioteca: Forneça o máximo possível de informações de diagnóstico úteis.
Algumas das coisas impressionantes que a biblioteca faz:
void zoog ( const std::map<std::string, int >& map) {
DEBUG_ASSERT (map. contains ( " foo " ), " expected key not found " , map);
}
ASSERT (vec.size() > min_items(), "vector doesn't have enough items", vec);
std::optional< float > get_param ();
float f = * ASSERT_VAL (get_param());
Tipos de afirmações:
Afirmações condicionais:
DEBUG_ASSERT : Verificado em Debug, mas não faz nada no lançamento (análogo à assert da biblioteca padrão)ASSERT : verificado em depuração e liberaçãoASSUME : verificado em depuração e serve como uma dica de otimização no lançamentoAfirmações incondicionais:
PANIC : gatilhos em depuração e liberaçãoUNREACHABLE : gatilhos em depuração, marcados como inacessíveis no lançamento Preferem baixa assert ?
Você pode ativar os aliases minúsculos debug_assert e assert aliases com -DLIBASSERT_LOWERCASE .
Resumo dos recursos:
ASSERT_EQ etc.DEBUG_ASSERT_VAL e ASSERT_VAL Variantes que retornam um valor para que possam ser integradas perfeitamente ao código, por exemplo FILE* f = ASSERT_VAL(fopen(path, "r") != nullptr) include (FetchContent)
FetchContent_Declare(
libassert
GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git
GIT_TAG v2.1.2 # <HASH or TAG>
)
FetchContent_MakeAvailable(libassert)
target_link_libraries (your_target libassert::assert)
# On windows copy libassert.dll to the same directory as the executable for your_target
if ( WIN32 )
add_custom_command (
TARGET your_target POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:libassert::assert>
$<TARGET_FILE_DIR:your_target>
)
endif () Certifique -se de configurar com -DCMAKE_BUILD_TYPE=Debug ou -DDCMAKE_BUILD_TYPE=RelWithDebInfo para obter informações de símbolos e linha.
No MacOS, é recomendável gerar um arquivo .dsym, consulte a logística da plataforma abaixo.
Para outras maneiras de usar a biblioteca, como através de gerentes de pacotes ou uma instalação em todo o sistema, consulte o uso abaixo.
Fundamentalmente, o papel das afirmações é verificar as suposições feitas no software e identificar violações próximas a suas fontes. A ferramenta de afirmação deve priorizar o fornecimento o máximo de informações e o contexto possível ao desenvolvedor para permitir a triagem rápida. Infelizmente, as ferramentas de idioma e biblioteca existentes fornecem informações de triagem muito limitadas.
Por exemplo, com asserções stdlib, uma afirmação como assert(n <= 12); Não fornece informações sobre a falha sobre por que ela falhou ou o que leva à sua falha. Fornecer um rastreamento de pilha e o valor de n Greatley melhora a triagem e a depuração. Idealmente, uma falha de afirmação deve fornecer informações de diagnóstico suficientes para que o programador não precise executar novamente um depurador para identificar o problema.
A versão 1 desta biblioteca foi uma exploração que analisava quanta informação e funcionalidade úteis poderiam ser embaladas em afirmações, além de fornecer uma interface rápida e fácil para o desenvolvedor.
A versão 2 desta biblioteca recebe lições aprendidas da versão 1 para criar uma ferramenta que eu pessoalmente achei indispensável em desenvolvimento.
O recurso mais importante que esta biblioteca suporta é a decomposição automática da expressão. Não há necessidade de ASSERT_LT ou outro aborrecimento, assert(vec.size() > 10); é entendido automaticamente, como exibido acima.
Os valores envolvidos nas expressões assert são exibidos. Diagnósticos redundantes como 2 => 2 são evitados.
DEBUG_ASSERT (map.count( 1 ) == 2);
Somente a expressão de assert completa é capaz de ser extraída de uma chamada macro. Mostrar quais partes da expressão correspondem a quais valores requer alguma análise de expressão básica. A gramática C ++ é ambígua, mas a maioria das expressões pode ser desambigada.
Todas as afirmações nesta biblioteca suportam mensagens de diagnóstico opcionais, bem como outras mensagens de diagnóstico arbitrárias.
FILE* f = ASSERT_VAL(fopen(path, " r " ) != nullptr , " Internal error with foobars " , errno, path); O manuseio especial é fornecido para errno , e o STRERROR é chamado automaticamente.
Nota: Diagnósticos extras são avaliados apenas no caminho de falha de uma afirmação.

Muito trabalho foi colocado em gerar traços bonitos e formatá -los o mais bem possível. O CPPTrace é usado como uma solução portátil e independente para o Stacktraces pré-C ++ 23. As configurações opcionais podem ser encontradas na documentação da biblioteca.
Um recurso que vale a pena notar é que, em vez de sempre imprimir caminhos completos, apenas o número mínimo de diretórios necessários para diferenciar caminhos são impressos.

Outro recurso que vale a pena apontar é que os traços de pilha dobrarão traços com uma recursão profunda:

O manipulador de afirmação aplica a sintaxe destacando sempre que se apropriado, como visto em todas as capturas de tela acima. Isso é para ajudar a melhorar a legibilidade.
Libassert suporta manipuladores de falha de asserção personalizados:
void handler (assert_type type, const assertion_info& assertion) {
throw std::runtime_error ( " Assertion failed: n " + assertion. to_string ());
}
int main () {
libassert::set_failure_handler (handler);
} Muito cuidado é dado à produção de stringificações de depuração de valores da maneira mais eficaz possível: seqüências de caracteres, caracteres, números, devem ser impressas como seria de esperar. Além disso, contêineres, tuplas, std :: opcional, ponteiros inteligentes, etc. são todos rigorosos para mostrar o máximo de informações possível. Se um operator<<(std::ostream& o, const S& s) , essa sobrecarga será chamada. Caso contrário, uma mensagem padrão será impressa. Além disso, é fornecido um ponto de personalização de stringification:
template <> struct libassert ::stringifier<MyObject> {
std::string stringify ( const MyObject& type) {
return ...;
}
};

Os valores de afirmação são impressos em hexadecimal ou binário, bem como decimal, se o hexademário/binário for usado em ambos os lados de uma expressão de afirmação:
ASSERT (get_mask() == 0b00001101);
Como as expressões já estão sendo decompostas automaticamente, você pode optar por ter comparações assinadas com a segurança de sinais com segurança com -DLIBASSERT_SAFE_COMPARISONS :
ASSERT ( 18446744073709551606ULL == - 10 );
O LibASsert fornece dois cabeçalhos <libassert/assert-catch2.hpp> e <libassert/assert-gtest.hpp> para uso com Catch2 e GoogleTest.
Exemplo de saída do gTest:

Mais informações abaixo.
O Libassert fornece três tipos de afirmações, cada uma variando um pouco, dependendo de quando deve ser verificada e como deve ser interpretado:
| Nome | Efeito |
|---|---|
DEBUG_ASSERT | Verificado em Debug, sem código de lançamento |
ASSERT | Verificado em trocas de depuração e liberação |
ASSUME | Verificado em Debug, if(!(expr)) { __builtin_unreachable(); } na versão |
Afirmações incondicionais
| Nome | Efeito |
|---|---|
PANIC | Gatilhos em depuração e liberação |
UNREACHABLE | Acionado em depuração, marcado como inacessível no lançamento. |
Um benefício para PANIC e UNREACHABLE sobre ASSERT(false, ...) é que o compilador obtém informações [[noreturn]] .
ASSUME marca o caminho de falha inacessível na liberação, potencialmente fornecendo informações úteis para o otimizador. Esse não é o comportamento padrão para todas as afirmações, porque a conseqüência imediata disso é que a falha de afirmação em -DNDEBUG pode levar ao UB e é melhor tornar isso muito explícito.
Variantes de afirmação que podem ser usadas em linha em uma expressão, como FILE* file = ASSERT_VAL(fopen(path, "r"), "Failed to open file"); , também estão disponíveis:
| Nome | Efeito |
|---|---|
DEBUG_ASSERT_VAL | Verificado em depuração, deve ser avaliado em depuração e liberação |
ASSERT_VAl | Verificado em trocas de depuração e liberação |
ASSUME_VAL | Verificado em Debug, if(!(expr)) { __builtin_unreachable(); } na versão |
NOTA: Mesmo no lançamento, constrói a expressão para DEBUG_ASSERT_VAL ainda deve ser avaliada, diferentemente de DEBUG_ASSERT . Obviamente, se o resultado não for utilizado e não produzir efeitos colaterais, ele será otimizado.
Desempenho: no que diz respeito ao desempenho do tempo de execução, o impacto no Callingites é muito mínimo sob -Og ou superior. O caminho rápido no código (ou seja, onde a afirmação não falha) será rápida. É necessário muito trabalho para processar falhas de afirmação quando elas acontecem. No entanto, como as falhas devem ser raras, isso não deve importar.
Velocidade de compilação:, há um custo de tempo de compilação associado a todas as instanciações de modelo necessárias para a magia desta biblioteca.
Outro:
Observação
Por causa da decomposição da expressão, ASSERT(1 = 2); compila.
Todas as funções de afirmação são macros. Aqui estão algumas pseudo-decididas para interface com elas:
void DEBUG_ASSERT (expression, [optional message], [optional extra diagnostics, ...]);
void ASSERT (expression, [optional message], [optional extra diagnostics, ...]);
void ASSUME (expression, [optional message], [optional extra diagnostics, ...]);
decltype ( auto ) DEBUG_ASSERT_VAL(expression, [optional message], [optional extra diagnostics, ...]);
decltype ( auto ) ASSERT_VAL (expression, [optional message], [optional extra diagnostics, ...]);
decltype ( auto ) ASSUME_VAL (expression, [optional message], [optional extra diagnostics, ...]);
void PANIC ([optional message], [optional extra diagnostics, ...]);
void UNREACHABLE ([optional message], [optional extra diagnostics, ...]); -DLIBASSERT_PREFIX_ASSERTIONS pode ser usado para prefixar essas macros com LIBASSERT_ . Isso é útil para encerrar as afirmações do Libassert.
-DLIBASSERT_LOWERCASE pode ser usado para ativar os aliases debug_assert e assert para DEBUG_ASSERT e ASSERT . Veja: Substituindo <Cassert>.
expression A expression é decomposta automaticamente para que as informações de diagnóstico possam ser fornecidas. O tipo resultante deve ser conversível em booleano.
A operação entre os lados esquerdo e direito da operação de nível superior na árvore de expressão é avaliado por um objeto de função.
Nota: Os operadores lógicos booleanos ( && e || ) não são decompostos por padrão devido a curto -circuito.
assertion message Uma mensagem de afirmação opcional pode ser fornecida. Se o primeiro argumento que segue a expressão de asserção, ou o primeiro argumento em pânico/inacessível, for qualquer tipo de string, ele será usado como mensagem (se você deseja o primeiro parâmetro, que é uma string, para ser um valor de diagnóstico extra, em vez disso, simplesmente passe uma sequência vazia primeiro, ou seja, ASSERT(foo, "", str); );
Nota: A expressão da mensagem de asserção é avaliada apenas no caminho de falha da afirmação.
extra diagnosticsUm número arbitrário de valores de diagnóstico extra pode ser fornecido. Eles são exibidos abaixo do diagnóstico da expressão se uma verificação falhar.
Nota: Diagnósticos extras são avaliados apenas no caminho de falha da afirmação.
Existe um manuseio especial quando errno é fornecido: o valor do strerror é exibido automaticamente.
Para facilitar a facilidade de integração nas variantes de código _VAL , que retornam um valor da expressão assert. O valor retornado é determinado da seguinte maneira:
ASSERT_VAL(foo()); ou ASSERT_VAL(false); ) na expressão de asserção, o valor da expressão será simplesmente retornado.== , != , < , <= , > , >= , && , || , ou ou qualquer atribuição ou atribuição composta, o valor do operando esquerdo é retornado.& , | , ^ , << , >> , ou qualquer operador binário com precedência acima do deslocamento de bits, o valor de toda a expressão é retornado. Ou seja, ASSERT_VAL(foo() > 2); Retorna o resultado calculado de foo() e ASSERT_VAL(x & y); Retorna o resultado calculado de x & y ;
Se o valor da expressão de asserção selecionado a ser retornado for um LValue, o tipo de chamada de asserção será uma referência de LValue. Se o valor da expressão de asserção for um RValue, o tipo de chamada será um RValue.
namespace libassert {
[[nodiscard]] std::string stacktrace (
int width = 0 ,
const color_scheme& scheme = get_color_scheme(),
std::size_t skip = 0
);
template < typename T> [[nodiscard]] std::string_view type_name () noexcept ;
template < typename T> [[nodiscard]] std::string pretty_type_name () noexcept ;
template < typename T> [[nodiscard]] std::string stringify ( const T& value);
}stacktrace : gera um rastreamento de pilha, formatos para a largura fornecida (0 para nenhuma formatação de largura)type_name : retorna o nome do tipo de tpretty_type_name : retorna o nome do tipo bonito para tstringify : produz uma rigidez de depuração de um valor namespace libassert {
void enable_virtual_terminal_processing_if_needed ();
inline constexpr int stdin_fileno = 0 ;
inline constexpr int stdout_fileno = 1 ;
inline constexpr int stderr_fileno = 2 ;
bool isatty ( int fd);
[[nodiscard]] int terminal_width ( int fd);
}enable_virtual_terminal_processing_if_needed : Ativar sequências de escape ANSI para terminais no Windows, necessários para saída de cores.isatty : retorna true se o descritor de arquivo corresponde a um terminalterminal_width : retorna a largura do terminal representado por FD ou 0 em erro namespace libassert {
// NOTE: string view underlying data should have static storage duration, or otherwise live as
// long as the scheme is in use
struct color_scheme {
std::string_view string, escape, keyword, named_literal, number, punctuation, operator_token,
call_identifier, scope_resolution_identifier, identifier, accent, unknown, reset;
static const color_scheme ansi_basic;
static const color_scheme ansi_rgb;
static const color_scheme blank;
};
void set_color_scheme ( const color_scheme&);
const color_scheme& get_color_scheme ();
} Por padrão, color_scheme::ansi_rgb é usado. Para desativar as cores, use color_scheme::blank .
set_color_scheme : define o esquema de cores para o manipulador de asserção padrão quando o stderr é um terminal namespace libassert {
void set_separator (std::string_view separator);
}set_separator : define o separador entre expressão e valor na saída de diagnóstico de asserção. Padrão: => . Nota: Não é segura para threads. namespace libassert {
enum class literal_format_mode {
infer, // infer literal formats based on the assertion condition
no_variations, // don't do any literal format variations, just default
fixed_variations // always use a fixed set of formats (in addition to the default format)
};
void set_literal_format_mode (literal_format_mode);
enum class literal_format : unsigned {
// integers and floats are decimal by default, chars are of course chars, and everything
// else only has one format that makes sense
default_format = 0 ,
integer_hex = 1 ,
integer_octal = 2 ,
integer_binary = 4 ,
integer_character = 8 , // format integers as characters and characters as integers
float_hex = 16 ,
};
[[nodiscard]] constexpr literal_format operator |(literal_format a, literal_format b);
void set_fixed_literal_format (literal_format);
}set_literal_format_mode : define se a biblioteca deve mostrar variações literais ou inferidasset_fixed_literal_format : defina uma configuração de formato literal fixo, altera automaticamente o literal_format_mode; Observe que o formato padrão sempre será usado junto com outros namespace libassert {
enum class path_mode {
full, // full path is used
disambiguated, // only enough folders needed to disambiguate are provided
basename, // only the file name is used
};
LIBASSERT_EXPORT void set_path_mode (path_mode mode);
}set_path_mode : define o modo de encurtamento do caminho para saída de asserção. Padrão: path_mode::disambiguated . namespace libassert {
enum class assert_type {
debug_assertion,
assertion,
assumption,
panic,
unreachable
};
struct LIBASSERT_EXPORT binary_diagnostics_descriptor {
std::string left_expression;
std::string right_expression;
std::string left_stringification;
std::string right_stringification;
};
struct extra_diagnostic {
std::string_view expression;
std::string stringification;
};
struct LIBASSERT_EXPORT assertion_info {
std::string_view macro_name;
assert_type type;
std::string_view expression_string;
std::string_view file_name;
std:: uint32_t line;
std::string_view function;
std::optional<std::string> message;
std::optional<binary_diagnostics_descriptor> binary_diagnostics;
std::vector<extra_diagnostic> extra_diagnostics;
size_t n_args;
std::string_view action () const ;
const cpptrace::raw_trace& get_raw_trace () const ;
const cpptrace::stacktrace& get_stacktrace () const ;
[[nodiscard]] std::string header ( int width = 0 , const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string tagline ( const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string location () const ;
[[nodiscard]] std::string statement ( const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string print_binary_diagnostics ( int width = 0 , const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string print_extra_diagnostics ( int width = 0 , const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string print_stacktrace ( int width = 0 , const color_scheme& scheme = get_color_scheme()) const ;
[[nodiscard]] std::string to_string ( int width = 0 , const color_scheme& scheme = get_color_scheme()) const ;
};
} Debug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars
debug_assert(open(path, 0) >= 0, ...);
Where:
open(path, 0) => -1
Extra diagnostics:
errno => 2 "No such file or directory"
path => "/home/foobar/baz"
Stack trace:
#1 demo.cpp:194 foo::baz()
#2 demo.cpp:172 void foo::bar<int>(std::pair<int, int>)
#3 demo.cpp:396 main
Debug Assertion failed : assertion_info.action()demo.cpp:194 : assertion_info.file_name e assertion_info.linevoid foo::baz() : assertion_info.pretty_functionInternal error with foobars : assertion_info.messagedebug_assert : assertion_info.macro_nameopen(path, 0) >= 0 : assertion_info.expression_string... : determinado por assertion_info.n_args que tem o número total de argumentos passados para a macro de afirmaçãoopen(path, 0) : assertion_info.binary_diagnostics.left_expression-1 : assertion_info.binary_diagnostics.left_stringification0 => 0 não é útil)errno : assertion_info.extra_diagnostics[0].expression2 "No such file or directory" : assertion_info.extra_diagnostics[0].stringificationassertion_info.get_stacktrace() ou assertion_info.get_raw_trace() para obter o rastreamento sem resolvê -loAjudantes:
assertion_info.header() :
Debug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars
debug_assert(open(path, 0) >= 0, ...);
Where:
open(path, 0) => -1
Extra diagnostics:
errno => 2 "No such file or directory"
path => "/home/foobar/baz"
assertion_info.tagline() :
Debug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars
assertion_info.location() :
Observação
O processamento do caminho será realizado de acordo com o modo de caminho
demo.cpp:194
assertion_info.statement() :
debug_assert(open(path, 0) >= 0, ...);
assertion_info.print_binary_diagnostics() :
Where:
open(path, 0) => -1
assertion_info.print_extra_diagnostics() :
Extra diagnostics:
errno => 2 "No such file or directory"
path => "/home/foobar/baz"
assertion_info.print_stacktrace() :
Stack trace:
#1 demo.cpp:194 foo::baz()
#2 demo.cpp:172 void foo::bar<int>(std::pair<int, int>)
#3 demo.cpp:396 main
Libassert fornece um ponto de personalização para tipos definidos pelo usuário:
template <> struct libassert ::stringifier<MyObject> {
std::string stringify ( const MyObject& type) {
return ...;
}
};Por padrão, todos os tipos definidos pelo usuário do tipo contêiner serão automaticamente rigorosos.
Além disso, LIBASSERT_USE_FMT pode ser usado para permitir que o libassert use fmt::formatter s.
Por fim, quaisquer tipos com um operator<< sobrecarga podem ser atingidos.
namespace libassert {
void set_failure_handler ( void (*handler)( const assertion_info&));
}set_failure_handler : define o manipulador de asserção para o programa.Um exemplo de manipulador de afirmação semelhante ao manipulador padrão:
void libassert_default_failure_handler ( const assertion_info& info) {
libassert::enable_virtual_terminal_processing_if_needed (); // for terminal colors on windows
std::string message = info. to_string (
libassert::terminal_width (libassert::stderr_fileno),
libassert::isatty (libassert::stderr_fileno)
? libassert::get_color_scheme ()
: libassert::color_scheme::blank
);
std::cerr << message << std::endl;
switch (info. type ) {
case libassert::assert_type::assertion:
case libassert::assert_type::debug_assertion:
case libassert::assert_type::assumption:
case libassert::assert_type::panic:
case libassert::assert_type::unreachable:
( void ) fflush (stderr);
std::abort ();
// Breaking here as debug CRT allows aborts to be ignored, if someone wants to make a
// debug build of this library
break ;
default :
std::cerr << " Critical error: Unknown libassert::assert_type " << std::endl;
std::abort ( 1 );
}
}Por padrão, Libassert aborta de todos os tipos de afirmação. No entanto, pode ser desejável lançar uma exceção de alguns ou todos os tipos de afirmação em vez de abortar.
Importante
Os manipuladores de falha não devem retornar para assert_type::panic e assert_type::unreachable .
Libassert apóia pontos de interrupção programáticos sobre a falha de afirmação em tornar as afirmações mais amigas do depurador, quebrando a linha de afirmação, em oposição a várias camadas profundas em uma pista de call:

Atualmente, essa funcionalidade está optada e pode ser ativada definindo LIBASSERT_BREAK_ON_FAIL . É melhor fazer isso como um sinalizador do compilador: -DLIBASSERT_BREAK_ON_FAIL ou /DLIBASSERT_BREAK_ON_FAIL .
Internamente, a biblioteca verifica a presa de um depurador antes de executar uma instrução para o ponto de interrupção do depurador. Por padrão, a verificação é executada apenas uma vez na primeira falha de asserção. Em alguns cenários, pode ser desejável configurar essa verificação para sempre ser executada, por exemplo, se você estiver usando um manipulador de asserção personalizado que lança uma exceção em vez de abortar e poderá se recuperar de uma falha de afirmação, permitindo falhas adicionais posteriormente e você apenas anexar uma parte do deputador através da execução do seu programa. Você pode usar libassert::set_debugger_check_mode para controlar como essa verificação é executada:
namespace libassert {
enum class debugger_check_mode {
check_once,
check_every_time,
};
void set_debugger_check_mode (debugger_check_mode mode) noexcept ;
}A biblioteca também expõe seus utilitários internos para definir pontos de interrupção e verificar se o programa estiver sendo depurado:
namespace libassert {
bool is_debugger_present () noexcept ;
}
# define LIBASSERT_BREAKPOINT () <...internals...>
# define LIBASSERT_BREAKPOINT_IF_DEBUGGING () <...internals...>Esta API imita a API da P2514, que foi aceita em C ++ 26.
Uma nota sobre constexpr : para CLANG e MSVC Libassert pode usar o Intrinsics do Compiler, no entanto, para a montagem em linha do GCC é necessária. A montagem em linha não é permitida nas funções Constexpr pré-C ++ 20, no entanto, o GCC suporta-o com um aviso após o GCC 10 e a biblioteca pode surgir esse aviso para o GCC 12.
Define:
LIBASSERT_USE_MAGIC_ENUM : use Magic Enum para rigificar valores de enumLIBASSERT_DECOMPOSE_BINARY_LOGICAL : decomponha && e ||LIBASSERT_SAFE_COMPARISONS : Ativar comparações assinadas com sinalização segura para expressões decompostasLIBASSERT_PREFIX_ASSERTIONS : prefixos todas as macros de asserção com LIBASSERT_LIBASSERT_USE_FMT : Ativa a integração libfmtLIBASSERT_NO_STRINGIFY_SMART_POINTER_OBJECTS : desabilita a stringificação de conteúdo de ponteiro inteligenteCmake:
LIBASSERT_USE_EXTERNAL_CPPTRACE : use um externam cpptrace em vez de adquirir a biblioteca com o FetchContentLIBASSERT_USE_EXTERNAL_MAGIC_ENUM : use um enum externam Magic em vez de adquirir a biblioteca com o FetchContentObservação
Devido ao pré-processador não-conforme da MSVC, não há uma maneira fácil de fornecer invólucros de afirmação. Para usar as integrações da biblioteca de testes /Zc:preprocessor .
Libassert fornece uma integração Catch2 em libassert/assert-catch2.hpp :
# include < libassert/assert-catch2.hpp >
TEST_CASE ( " 1 + 1 is 2 " ) {
ASSERT ( 1 + 1 == 3 );
}
Atualmente, a única macro fornecida é ASSERT , o que executará um REQUIRE internamente.
Nota: Antes da v3.6.0, os códigos de cores ANSI interferem na embalagem da linha do Catch2, para que a cor seja desativada nas versões mais antigas.
Libassert fornece uma integração GTest em libassert/assert-gtest.hpp :
# include < libassert/assert-gtest.hpp >
TEST (Addition, Arithmetic) {
ASSERT ( 1 + 1 == 3 );
}
Atualmente, Libassert fornece as Macros ASSERT e EXPECT para o GTEST.
Isso não é tão bonito quanto eu gostaria, no entanto, faz o trabalho.
Esta biblioteca metas> = C ++ 17 e suporta todos os principais compiladores e todas as principais plataformas (Linux, MacOS, Windows e Mingw).
NOTA: A biblioteca depende de algumas extensões do compilador e recursos específicos do compilador para que não seja compatível com -pedantic .
Com CMake FetchContent:
include (FetchContent)
FetchContent_Declare(
libassert
GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git
GIT_TAG v2.1.2 # <HASH or TAG>
)
FetchContent_MakeAvailable(libassert)
target_link_libraries (your_target libassert::assert)NOTA: No Windows e MacOS, é recomendado algum trabalho extra, consulte a logística da plataforma abaixo.
Certifique -se de configurar com -DCMAKE_BUILD_TYPE=Debug ou -DDCMAKE_BUILD_TYPE=RelWithDebInfo para obter informações de símbolos e linha.
git clone https://github.com/jeremy-rifkin/libassert.git
git checkout v2.1.2
mkdir libassert/build
cd libassert/build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j
sudo make installUsando através do cmake:
find_package (libassert REQUIRED)
target_link_libraries (<your target > libassert::assert) Certifique -se de configurar com -DCMAKE_BUILD_TYPE=Debug ou -DDCMAKE_BUILD_TYPE=RelWithDebInfo para obter informações de símbolos e linha.
Ou compilar com -lassert :
g++ main.cpp -o main -g -Wall -lassert
./mainSe você receber um erro ao longo das linhas de
error while loading shared libraries: libassert.so: cannot open shared object file: No such file or directory
Você pode ter que executar sudo /sbin/ldconfig para criar os links necessários e atualizar caches para que o sistema possa encontrar libcpptrace.so (eu tive que fazer isso no Ubuntu). Somente ao instalar em todo o sistema. Normalmente, o seu manual de embalagem faz isso para você ao instalar novas bibliotecas.
git clone https: // github.com / jeremy - rifkin / libassert.git
git checkout v2. 1.2
mkdir libassert / build
cd libassert / build
cmake . . - DCMAKE_BUILD_TYPE = Release
msbuild libassert.sln
msbuild INSTALL.vcxprojNOTA: Você precisará executar como administrador em um desenvolvedor PowerShell ou usar vcvarsall.bat distribuído com o Visual Studio para obter as variáveis de ambiente corretas definidas.
Para instalar apenas para o usuário local (ou qualquer prefixo personalizado):
git clone https://github.com/jeremy-rifkin/libassert.git
git checkout v2.1.2
mkdir libassert/build
cd libassert/build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= $HOME /wherever
make -j
sudo make installUsando através do cmake:
find_package (libassert REQUIRED PATHS $ENV{HOME} /wherever)
target_link_libraries (<your target > libassert::assert)Usando manualmente:
g++ main.cpp -o main -g -Wall -I$HOME/wherever/include -L$HOME/wherever/lib -lassert
Para usar a biblioteca sem o CMake, siga as instruções de instalação primeiro em instalação em todo o sistema, instalação local do usuário ou gerentes de pacotes.
Use os seguintes argumentos para compilar com Libassert:
| Compilador | Plataforma | Dependências |
|---|---|---|
| GCC, Clang, Intel, etc. | Linux/MacOS/Unix | -libassert -I[path] [cpptrace args] |
| Mingw | Windows | -libassert -I[path] [cpptrace args] |
| msvc | Windows | assert.lib /I[path] [cpptrace args] |
| Clang | Windows | -libassert -I[path] [cpptrace args] |
Para o espaço reservado [path] em -I[path] e /I[path] , especifique o caminho para a pasta de inclusão que contém libassert/assert.hpp .
Se você estiver vinculando estaticamente, também precisará especificar -DLIBASSERT_STATIC_DEFINE .
Para o espaço reservado [cpptrace args] consulte a documentação do CPPTRACE.
Libassert está disponível através de Conan em https://conan.io/center/recipes/libassert.
[requires]
libassert/2.1.2
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout
# ...
find_package (libassert REQUIRED)
# ...
target_link_libraries (YOUR_TARGET libassert::assert) vcpkg install libassert
find_package (libassert CONFIG REQUIRED)
target_link_libraries (YOUR_TARGET PRIVATE libassert::assert)Windows e MacOS exigem um pouco de trabalho extra para colocar tudo no lugar certo
Copiando a biblioteca .dll no Windows:
# Copy the assert.dll on windows to the same directory as the executable for your_target.
# Not required if static linking.
if ( WIN32 )
add_custom_command (
TARGET your_target POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:libassert::assert>
$<TARGET_FILE_DIR:your_target>
)
endif ()No MacOS, é recomendável gerar um arquivo DSYM contendo informações de depuração para o seu programa:
No xcode cmake, isso pode ser feito com
set_target_properties (your_target PROPERTIES XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym" ) E fora do xcode, isso pode ser feito com dsymutil yourbinary :
# Create a .dSYM file on macos. Currently required, but hopefully not for long
if ( APPLE )
add_custom_command (
TARGET your_target
POST_BUILD
COMMAND dsymutil $<TARGET_FILE:your_target>
)
endif () Esta biblioteca não é um substituto para <cassert> . -DLIBASSERT_LOWERCASE pode ser usado para criar aliases minúsculas para as macros de asserção, mas esteja ciente de que ASSERT de Libassert ainda está verificada na liberação. Para substituir <cassert> Use pelo Libassert, substitua assert por DEBUG_ASSERT ou crie um alias ao longo das seguintes linhas:
# define assert (...) DEBUG_ASSERT(__VA_ARGS__) Uma coisa a estar ciente: substituir assert de Cassert tecnicamente não é permitida pelo padrão, mas isso não deve ser um problema para nenhum compilador sã.
Não, ainda não.
cpp-sort Mesmo com construções como assert_eq , falta diagnósticos de afirmação. Por exemplo, na ferrugem, os valores esquerdo e direito são exibidos, mas não as próprias expressões:
fn main ( ) {
let count = 4 ;
assert_eq ! ( count , 2 ) ;
} thread 'main' panicked at 'assertion failed: `(left == right)`
left: `4`,
right: `2`', /app/example.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Isso não é tão útil quanto poderia ser.
Funcionalidade Outras línguas / suas bibliotecas padrão fornecem:
| C/C ++ | Ferrugem | C# | Java | Python | JavaScript | Libassert | |
|---|---|---|---|---|---|---|---|
| String de expressão | ✔️ | ✔️ | |||||
| Localização | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Rastreamento da pilha | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | |
| Mensagem de afirmação | ** | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Diagnóstico extra | * | * | * | * | ✔️ | ||
| Especializações binárias | ✔️ | ✔️ | ✔️ | ||||
| Decomposição automática de expressão | ✔️ | ||||||
| Strings de sub-expressão | ✔️ |
* : Possível através da formatação da string, mas isso é sub-ideal.
** : assert(expression && "message") é comumente usado, mas isso é sub-ideal e permite apenas mensagens literais de string.
Extras:
| C/C ++ | Ferrugem | C# | Java | Python | JavaScript | Libassert | |
|---|---|---|---|---|---|---|---|
| Destaque da sintaxe | ? | ✔️ | |||||
| Consistência de formatação literal | ✔️ | ||||||
| Strings de expressão e valores de expressão em todos os lugares | ✔️ | ||||||
| Retornar valores da afirmação para permitir que as afirmações sejam integradas em expressões embutidas | ✔️ |