주장 기능
c에는 매크로가 assert
있으며 작동하는 동안 주로 문자열을 인쇄하는 데 좋은 방법이 없기 때문에 이와 같은 그림이 아닙니다. 결과적으로 더 나은 버전의 assert
C ++ 를 만들려고했습니다 . 그러나 나는 그것을 과도하게 설계하여 컴파일 시간을 늦추는 무언가와 사용하지 않을 무언가를 남겼습니다. 어떻게 개선 할 수 있습니까?
assert.hh
#ifndef ASSERT_HH
#define ASSERT_HH
#include <iostream>
#include <utility>
#include <tuple>
namespace turtle
{
/* these macros are needed because #__VA_ARGS__ will include the comma in the string
* for example: macro(...) __VA_ARGS__
* macro(a,b) -> expands to "a,b"
* instead of "a","b"
*/
#define TURTLE_FIRST_(a, ...) a
#define TURTLE_SECOND_(a, b, ...) b
#define TURTLE_FIRST(...) TURTLE_FIRST_(__VA_ARGS__,)
#define TURTLE_SECOND(...) TURTLE_SECOND_(__VA_ARGS__,)
#define TURTLE_EMPTY()
#define TURTLE_EVAL(...) TURTLE_EVAL1024(__VA_ARGS__)
#define TURTLE_EVAL1024(...) TURTLE_EVAL512(TURTLE_EVAL512(__VA_ARGS__))
#define TURTLE_EVAL512(...) TURTLE_EVAL256(TURTLE_EVAL256(__VA_ARGS__))
#define TURTLE_EVAL256(...) TURTLE_EVAL128(TURTLE_EVAL128(__VA_ARGS__))
#define TURTLE_EVAL128(...) TURTLE_EVAL64(TURTLE_EVAL64(__VA_ARGS__))
#define TURTLE_EVAL64(...) TURTLE_EVAL32(TURTLE_EVAL32(__VA_ARGS__))
#define TURTLE_EVAL32(...) TURTLE_EVAL16(TURTLE_EVAL16(__VA_ARGS__))
#define TURTLE_EVAL16(...) TURTLE_EVAL8(TURTLE_EVAL8(__VA_ARGS__))
#define TURTLE_EVAL8(...) TURTLE_EVAL4(TURTLE_EVAL4(__VA_ARGS__))
#define TURTLE_EVAL4(...) TURTLE_EVAL2(TURTLE_EVAL2(__VA_ARGS__))
#define TURTLE_EVAL2(...) TURTLE_EVAL1(TURTLE_EVAL1(__VA_ARGS__))
#define TURTLE_EVAL1(...) __VA_ARGS__
#define TURTLE_DEFER1(m) m TURTLE_EMPTY()
#define TURTLE_DEFER2(m) m TURTLE_EMPTY TURTLE_EMPTY()()
#define TURTLE_IS_PROBE(...) TURTLE_SECOND(__VA_ARGS__, 0)
#define TURTLE_PROBE() ~, 1
#define TURTLE_CAT(a, b) a ## b
#define TURTLE_NOT(x) TURTLE_IS_PROBE(TURTLE_CAT(TURTLE__NOT_, x))
#define TURTLE__NOT_0 TURTLE_PROBE()
#define TURTLE_BOOL(x) TURTLE_NOT(TURTLE_NOT(x))
#define TURTLE_IF_ELSE(condition) TURTLE__IF_ELSE(TURTLE_BOOL(condition))
#define TURTLE__IF_ELSE(condition) TURTLE_CAT(TURTLE__IF_, condition)
#define TURTLE__IF_1(...) __VA_ARGS__ TURTLE__IF_1_ELSE
#define TURTLE__IF_0(...) TURTLE__IF_0_ELSE
#define TURTLE__IF_1_ELSE(...)
#define TURTLE__IF_0_ELSE(...) __VA_ARGS__
#define TURTLE_COMMA ,
#define TURTLE_HAS_ARGS(...) TURTLE_BOOL(TURTLE_FIRST(TURTLE__END_OF_ARGUMENTS_ __VA_ARGS__)())
#define TURTLE__END_OF_ARGUMENTS_() 0
#define TURTLE_MAP(m, first, ...) \
m(first) \
TURTLE_IF_ELSE(TURTLE_HAS_ARGS(__VA_ARGS__))( \
TURTLE_COMMA TURTLE_DEFER2(TURTLE__MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define TURTLE__MAP() TURTLE_MAP
#define TURTLE__STRINGIZE(x) TURTLE___STRINGIZE(x)
#define TURTLE___STRINGIZE(x) #x
#define TURTLE_STRINGIZE(x) __FILE__ " line " TURTLE__STRINGIZE(__LINE__) ": assertion {" #x
template<typename... Args, typename Text>
constexpr auto assert_basic(const std::tuple<Args...> &arg, const Text &text)
{
if constexpr (sizeof...(Args) == 1) {
if (!std::get<0>(arg)) {
std::cerr << text << "} failed\n";
return true;
}
} else {
if (!std::get<0>(arg)) {
std::cerr << text << "} failed -> "
<< std::get<1>(arg) << '\n';
return true;
}
}
return false;
}
template<std::size_t Index, typename Result, typename... Args>
constexpr auto assert_tuple_args_helper(const Result &result, const std::tuple<Args...> &tuple_args_basic)
{
if constexpr (Index >= sizeof...(Args)) {
return result;
} else if constexpr (Index + 1 >= sizeof...(Args) &&
std::is_convertible_v<std::tuple_element_t<Index, std::decay_t<decltype(tuple_args_basic)>>, int>) {
return assert_tuple_args_helper<Index + 1>(std::tuple_cat(result,
std::tuple<decltype(std::tuple{
std::get<Index>(tuple_args_basic)})>{
std::tuple{std::get<Index>(
tuple_args_basic)}}),
tuple_args_basic);
} else if constexpr (
std::is_convertible_v<std::tuple_element_t<Index, std::decay_t<decltype(tuple_args_basic)>>, int>
&&
std::is_convertible_v<std::tuple_element_t<Index + 1, std::decay_t<decltype(tuple_args_basic)>>, int>) {
return assert_tuple_args_helper<Index + 1>(
std::tuple_cat(result, std::tuple<decltype(std::tuple{std::get<Index>(tuple_args_basic)})>{
std::tuple{std::get<Index>(tuple_args_basic)}}), tuple_args_basic);
} else if constexpr (
std::is_convertible_v<std::tuple_element_t<Index, std::decay_t<decltype(tuple_args_basic)>>, int>
&& !std::is_convertible_v<std::tuple_element_t<
Index + 1, std::decay_t<decltype(tuple_args_basic)>>, int>) {
return assert_tuple_args_helper<Index + 2>(
std::tuple_cat(result, std::tuple<decltype(std::tuple{std::get<Index>(tuple_args_basic),
std::get<Index + 1>(
tuple_args_basic)})>
{std::tuple{std::get<Index>(tuple_args_basic),
std::get<Index + 1>(
tuple_args_basic)}}), tuple_args_basic);
}
}
template<std::size_t TupleIndex, typename Result, typename... Args>
constexpr auto assert_tuple_text_indices_helper(const Result& result, std::size_t old_index, const std::tuple<Args...>& tuple)
{
if constexpr (TupleIndex < std::tuple_size_v<std::decay_t<decltype(tuple)>>) {
std::size_t next_index = old_index + std::tuple_size_v<std::decay_t<decltype(std::get<TupleIndex>(tuple))>>;
return assert_tuple_text_indices_helper<TupleIndex+1>(std::tuple_cat(result, std::tuple{old_index}), next_index, tuple);
} else {
return result;
}
}
template<typename Text, typename... Args>
constexpr auto assert(const Text& text, Args &&... args)
{
const auto &tuple_args = assert_tuple_args_helper<0>(std::tuple{}, std::tuple{args...});
constexpr auto tuple_text_indices = assert_tuple_text_indices_helper<0>(std::tuple{}, 0, decltype(tuple_args){});
[&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
if ((assert_basic(std::get<Indices>(tuple_args), text[std::get<Indices>(tuple_text_indices)]) | ...)) {
std::exit(1);
}
}(std::make_index_sequence<std::tuple_size_v<std::decay_t<decltype(tuple_args)>>>{});
}
}
#define assert(...) turtle::assert(std::array{TURTLE_EVAL(TURTLE_MAP(TURTLE_STRINGIZE, __VA_ARGS__))}, __VA_ARGS__)
#endif /* ASSERT_HH */
main.cc
#include "assert.hh"
/* testing */
int main()
{
int a, b, c, d;
std::cin >> a >> b >> c >> d;
assert(__LINE__ % 2 == 1, a < b, "a must be less than b", b < c, "b must be less than c", c < d, "c must be less than d");
assert(__LINE__ % 2 == 1, __FILE__[2] < 80);
}
답변
2 Quuxplusone
성능 병목 현상을 찾고있을 때라는 부분에 집중하는 것이 도움이 될 수 있습니다 TURTLE
. ;) 그러나 나는 그것이 당신의 주요 문제라고 생각조차하지 않습니다.
주장에 메시지를 첨부하기 만하면되는 것 같습니다.
#define myAssert(x, msg) assert((x) && msg)
이것은 이중 괄호가 필요한 호출에서 항상 괄호를 두 배로 늘리도록 성실한 한 작동합니다.
myAssert(x < 2, "x is too large");
myAssert((foo<int,int> < 2), "foo<int,int> is too large");
괄호를 제거하려면이 문제를 "팩에서 마지막 매크로 인수를 어떻게 추출합니까?"로 줄 이겠습니다. 한 현명한 답변자는 그 질문 을 무시 하고 메시지를 첫 번째 인수로 넣으 라고 제안합니다 .
#define myAssert(msg, ...) assert((__VA_ARGS__) && msg)
myAssert("x is too large", x < 2);
myAssert("foo<int,int> is too large", foo<int,int> < 2);