1 // Copyright 2020 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ 16 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ 17 18 #include <array> 19 #include <cstdio> 20 #include <sstream> 21 #include <string> 22 23 #include "absl/base/port.h" 24 #include "absl/strings/internal/str_format/arg.h" 25 #include "absl/strings/internal/str_format/checker.h" 26 #include "absl/strings/internal/str_format/parser.h" 27 #include "absl/types/span.h" 28 29 namespace absl { 30 ABSL_NAMESPACE_BEGIN 31 32 class UntypedFormatSpec; 33 34 namespace str_format_internal { 35 36 class BoundConversion : public FormatConversionSpecImpl { 37 public: arg()38 const FormatArgImpl* arg() const { return arg_; } set_arg(const FormatArgImpl * a)39 void set_arg(const FormatArgImpl* a) { arg_ = a; } 40 41 private: 42 const FormatArgImpl* arg_; 43 }; 44 45 // This is the type-erased class that the implementation uses. 46 class UntypedFormatSpecImpl { 47 public: 48 UntypedFormatSpecImpl() = delete; 49 UntypedFormatSpecImpl(string_view s)50 explicit UntypedFormatSpecImpl(string_view s) 51 : data_(s.data()), size_(s.size()) {} UntypedFormatSpecImpl(const str_format_internal::ParsedFormatBase * pc)52 explicit UntypedFormatSpecImpl( 53 const str_format_internal::ParsedFormatBase* pc) 54 : data_(pc), size_(~size_t{}) {} 55 has_parsed_conversion()56 bool has_parsed_conversion() const { return size_ == ~size_t{}; } 57 str()58 string_view str() const { 59 assert(!has_parsed_conversion()); 60 return string_view(static_cast<const char*>(data_), size_); 61 } parsed_conversion()62 const str_format_internal::ParsedFormatBase* parsed_conversion() const { 63 assert(has_parsed_conversion()); 64 return static_cast<const str_format_internal::ParsedFormatBase*>(data_); 65 } 66 67 template <typename T> Extract(const T & s)68 static const UntypedFormatSpecImpl& Extract(const T& s) { 69 return s.spec_; 70 } 71 72 private: 73 const void* data_; 74 size_t size_; 75 }; 76 77 template <typename T, FormatConversionCharSet...> 78 struct MakeDependent { 79 using type = T; 80 }; 81 82 // Implicitly convertible from `const char*`, `string_view`, and the 83 // `ExtendedParsedFormat` type. This abstraction allows all format functions to 84 // operate on any without providing too many overloads. 85 template <FormatConversionCharSet... Args> 86 class FormatSpecTemplate 87 : public MakeDependent<UntypedFormatSpec, Args...>::type { 88 using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type; 89 90 public: 91 #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 92 93 // Honeypot overload for when the string is not constexpr. 94 // We use the 'unavailable' attribute to give a better compiler error than 95 // just 'method is deleted'. 96 FormatSpecTemplate(...) // NOLINT 97 __attribute__((unavailable("Format string is not constexpr."))); 98 99 // Honeypot overload for when the format is constexpr and invalid. 100 // We use the 'unavailable' attribute to give a better compiler error than 101 // just 'method is deleted'. 102 // To avoid checking the format twice, we just check that the format is 103 // constexpr. If is it valid, then the overload below will kick in. 104 // We add the template here to make this overload have lower priority. 105 template <typename = void> 106 FormatSpecTemplate(const char* s) // NOLINT 107 __attribute__(( 108 enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"), 109 unavailable( 110 "Format specified does not match the arguments passed."))); 111 112 template <typename T = void> FormatSpecTemplate(string_view s)113 FormatSpecTemplate(string_view s) // NOLINT 114 __attribute__((enable_if(str_format_internal::EnsureConstexpr(s), 115 "constexpr trap"))) { 116 static_assert(sizeof(T*) == 0, 117 "Format specified does not match the arguments passed."); 118 } 119 120 // Good format overload. FormatSpecTemplate(const char * s)121 FormatSpecTemplate(const char* s) // NOLINT 122 __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) 123 : Base(s) {} 124 FormatSpecTemplate(string_view s)125 FormatSpecTemplate(string_view s) // NOLINT 126 __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) 127 : Base(s) {} 128 129 #else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 130 131 FormatSpecTemplate(const char* s) : Base(s) {} // NOLINT 132 FormatSpecTemplate(string_view s) : Base(s) {} // NOLINT 133 134 #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 135 136 template < 137 FormatConversionCharSet... C, 138 typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type, 139 typename = typename std::enable_if<AllOf(Contains(Args, 140 C)...)>::type> FormatSpecTemplate(const ExtendedParsedFormat<C...> & pc)141 FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT 142 : Base(&pc) {} 143 }; 144 145 class Streamable { 146 public: Streamable(const UntypedFormatSpecImpl & format,absl::Span<const FormatArgImpl> args)147 Streamable(const UntypedFormatSpecImpl& format, 148 absl::Span<const FormatArgImpl> args) 149 : format_(format) { 150 if (args.size() <= ABSL_ARRAYSIZE(few_args_)) { 151 for (size_t i = 0; i < args.size(); ++i) { 152 few_args_[i] = args[i]; 153 } 154 args_ = absl::MakeSpan(few_args_, args.size()); 155 } else { 156 many_args_.assign(args.begin(), args.end()); 157 args_ = many_args_; 158 } 159 } 160 161 std::ostream& Print(std::ostream& os) const; 162 163 friend std::ostream& operator<<(std::ostream& os, const Streamable& l) { 164 return l.Print(os); 165 } 166 167 private: 168 const UntypedFormatSpecImpl& format_; 169 absl::Span<const FormatArgImpl> args_; 170 // if args_.size() is 4 or less: 171 FormatArgImpl few_args_[4] = {FormatArgImpl(0), FormatArgImpl(0), 172 FormatArgImpl(0), FormatArgImpl(0)}; 173 // if args_.size() is more than 4: 174 std::vector<FormatArgImpl> many_args_; 175 }; 176 177 // for testing 178 std::string Summarize(UntypedFormatSpecImpl format, 179 absl::Span<const FormatArgImpl> args); 180 bool BindWithPack(const UnboundConversion* props, 181 absl::Span<const FormatArgImpl> pack, BoundConversion* bound); 182 183 bool FormatUntyped(FormatRawSinkImpl raw_sink, 184 UntypedFormatSpecImpl format, 185 absl::Span<const FormatArgImpl> args); 186 187 std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, 188 absl::Span<const FormatArgImpl> args); 189 190 std::string FormatPack(const UntypedFormatSpecImpl format, 191 absl::Span<const FormatArgImpl> args); 192 193 int FprintF(std::FILE* output, UntypedFormatSpecImpl format, 194 absl::Span<const FormatArgImpl> args); 195 int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format, 196 absl::Span<const FormatArgImpl> args); 197 198 // Returned by Streamed(v). Converts via '%s' to the std::string created 199 // by std::ostream << v. 200 template <typename T> 201 class StreamedWrapper { 202 public: StreamedWrapper(const T & v)203 explicit StreamedWrapper(const T& v) : v_(v) { } 204 205 private: 206 template <typename S> 207 friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( 208 const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, 209 FormatSinkImpl* out); 210 const T& v_; 211 }; 212 213 } // namespace str_format_internal 214 ABSL_NAMESPACE_END 215 } // namespace absl 216 217 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ 218