blob: 1b4e6d519929464605b1d07a5cafbc8f41b1f2dc [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <fmt/format.h>
#include <glog/logging.h>
#include <cstddef>
#include <ios>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
namespace doris {
[[noreturn]] void doris_check_fail(std::string_view message);
namespace detail {
template <typename T>
concept OstreamPrintable = requires(std::ostream& os, const T& value) { os << value; };
class DorisCheckMessage {
public:
explicit DorisCheckMessage(std::string_view message) { _stream << message; }
template <typename T>
DorisCheckMessage& operator<<(const T& value) {
_stream << value;
return *this;
}
DorisCheckMessage& operator<<(std::ostream& (*func)(std::ostream&)) {
func(_stream);
return *this;
}
DorisCheckMessage& operator<<(std::ios& (*func)(std::ios&)) {
func(_stream);
return *this;
}
DorisCheckMessage& operator<<(std::ios_base& (*func)(std::ios_base&)) {
func(_stream);
return *this;
}
[[noreturn]] void fail() { doris_check_fail(_stream.str()); }
private:
std::ostringstream _stream;
};
class DorisCheckMessageVoidify {
public:
[[noreturn]] void operator&(DorisCheckMessage& message) const { message.fail(); }
[[noreturn]] void operator&(DorisCheckMessage&& message) const { message.fail(); }
};
class DorisCheckResult {
public:
explicit DorisCheckResult(bool ok) : _ok(ok) {}
explicit DorisCheckResult(std::string message) : _ok(false), _message(std::move(message)) {}
bool ok() const { return _ok; }
const std::string& message() const { return _message; }
private:
bool _ok;
std::string _message;
};
template <typename T>
std::string doris_check_value_to_string(const T& value) {
if constexpr (std::is_same_v<std::decay_t<T>, std::nullptr_t>) {
return "nullptr";
} else if constexpr (std::is_same_v<std::decay_t<T>, bool>) {
return value ? "true" : "false";
} else if constexpr (OstreamPrintable<T>) {
std::ostringstream oss;
oss << std::boolalpha << value;
return oss.str();
} else {
return "<unprintable>";
}
}
template <typename Lhs, typename Rhs, typename Comparator>
DorisCheckResult doris_check_binary_op_result(const Lhs& lhs, const Rhs& rhs,
std::string_view lhs_expr, std::string_view rhs_expr,
std::string_view op_expr, Comparator comparator) {
if (static_cast<bool>(comparator(lhs, rhs))) {
return DorisCheckResult(true);
}
return DorisCheckResult(fmt::format("Check failed: {} {} {} ({} vs {})", lhs_expr, op_expr,
rhs_expr, doris_check_value_to_string(lhs),
doris_check_value_to_string(rhs)));
}
} // namespace detail
} // namespace doris
// core in Debug mode, exception in Release mode.
#define DORIS_CHECK(stmt) \
if (bool _doris_check_ok = static_cast<bool>(stmt); _doris_check_ok) { \
} else [[unlikely]] \
::doris::detail::DorisCheckMessageVoidify() & \
::doris::detail::DorisCheckMessage("Check failed: " #stmt)
// Use DORIS_CHECK_* only for invariants that must also be checked in Release builds.
// Keep DCHECK_* in loops or other hot paths where Release checks would add overhead.
#ifndef NDEBUG
#define DORIS_CHECK_EQ(val1, val2) DCHECK_EQ(val1, val2)
#define DORIS_CHECK_NE(val1, val2) DCHECK_NE(val1, val2)
#define DORIS_CHECK_LT(val1, val2) DCHECK_LT(val1, val2)
#define DORIS_CHECK_LE(val1, val2) DCHECK_LE(val1, val2)
#define DORIS_CHECK_GT(val1, val2) DCHECK_GT(val1, val2)
#define DORIS_CHECK_GE(val1, val2) DCHECK_GE(val1, val2)
#else
#define DORIS_CHECK_BINARY_OP(val1, val2, op, op_str) \
if (auto _doris_check_result = ::doris::detail::doris_check_binary_op_result( \
(val1), (val2), #val1, #val2, op_str, \
[](const auto& _doris_check_lhs, const auto& _doris_check_rhs) { \
return _doris_check_lhs op _doris_check_rhs; \
}); \
_doris_check_result.ok()) { \
} else [[unlikely]] \
::doris::detail::DorisCheckMessageVoidify() & \
::doris::detail::DorisCheckMessage(_doris_check_result.message())
#define DORIS_CHECK_EQ(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, ==, "==")
#define DORIS_CHECK_NE(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, !=, "!=")
#define DORIS_CHECK_LT(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, <, "<")
#define DORIS_CHECK_LE(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, <=, "<=")
#define DORIS_CHECK_GT(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, >, ">")
#define DORIS_CHECK_GE(val1, val2) DORIS_CHECK_BINARY_OP(val1, val2, >=, ">=")
#endif