アサート機能
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)
これは、二重の親を必要とする呼び出しで常に括弧を2倍にすることに注意を払っている限り機能します。
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);