1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2 // -*- mode: C++ -*- 3 // 4 // Copyright 2021-2024 Google LLC 5 // 6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the 7 // "License"); you may not use this file except in compliance with the 8 // License. You may obtain a copy of the License at 9 // 10 // https://llvm.org/LICENSE.txt 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 // 18 // Author: Giuliano Procida 19 20 #ifndef STG_ERROR_H_ 21 #define STG_ERROR_H_ 22 23 #include <exception> 24 #include <iostream> 25 #include <optional> 26 #include <ostream> 27 #include <sstream> 28 #include <string> 29 #include <system_error> 30 31 namespace stg { 32 33 class Exception : public std::exception { 34 public: Exception(const std::string & message)35 explicit Exception(const std::string& message) { 36 Add(message); 37 } 38 what()39 const char* what() const noexcept(true) final { 40 return message_.c_str(); 41 } 42 Add(const std::string & message)43 void Add(const std::string& message) { 44 (message_ += message) += '\n'; 45 } 46 47 private: 48 std::string message_; 49 }; 50 51 // Coded to give compilers a chance of making `Check(ok) << foo;` as efficient 52 // as `if (!ok) { Die() << foo; }`. 53 class Check { 54 public: 55 // These functions are all small and inlinable. Check(bool ok)56 explicit Check(bool ok) 57 : os_(ok ? std::optional<std::ostringstream>() 58 : std::make_optional<std::ostringstream>()) {} noexcept(false)59 ~Check() noexcept(false) { 60 if (os_) { 61 Throw(*os_); 62 } 63 } 64 65 template <typename T> 66 Check& operator<<(const T& t) { 67 if (os_) { 68 *os_ << t; 69 } 70 return *this; 71 } 72 73 private: 74 std::optional<std::ostringstream> os_; 75 76 // This helper is too large to inline. Throw(const std::ostringstream & os)77 [[noreturn]] static void Throw(const std::ostringstream& os) { 78 throw Exception(os.str()); 79 } 80 }; 81 82 class Die { 83 public: ~Die()84 [[noreturn]] ~Die() noexcept(false) { 85 throw Exception(os_.str()); 86 } 87 88 template <typename T> 89 Die& operator<<(const T& t) { 90 os_ << t; 91 return *this; 92 } 93 94 private: 95 std::ostringstream os_; 96 }; 97 98 class Warn { 99 public: ~Warn()100 ~Warn() { 101 std::cerr << "warning: " << os_.str() << '\n'; 102 } 103 104 template <typename T> 105 Warn& operator<<(const T& t) { 106 os_ << t; 107 return *this; 108 } 109 110 private: 111 std::ostringstream os_; 112 }; 113 114 struct Error { ErrorError115 explicit Error(int number) : number(number) {} 116 int number; 117 }; 118 119 inline std::ostream& operator<<(std::ostream& os, Error error) { 120 return os << std::system_error(error.number, std::generic_category()).what(); 121 } 122 123 } // namespace stg 124 125 #endif // STG_ERROR_H_ 126