@@ -221,6 +221,60 @@ struct StringPrinter {
221221 }
222222};
223223
224+ // A wrapper around `RawSink` that can be passed to the user's
225+ // `FuzzTestPrintSourceCode` function, a FTADLE extension point for custom
226+ // printers. This is so that the function can look more like `AbslStringify`,
227+ // which is parameterized by the sink type and passes a pointer to the sink to
228+ // `absl::Format()`.
229+ struct RawSinkWrapper {
230+ domain_implementor::RawSink& raw_sink;
231+
232+ friend void AbslFormatFlush (RawSinkWrapper* absl_nonnull sink,
233+ absl::string_view part) {
234+ absl::Format (sink->raw_sink , " %s" , part);
235+ }
236+ };
237+
238+ template <typename T, typename = void >
239+ struct HasCustomSourceCodePrinter : std::false_type {};
240+
241+ template <typename T>
242+ struct HasCustomSourceCodePrinter <
243+ T, std::enable_if_t <std::is_void<decltype (FuzzTestPrintSourceCode(
244+ std::declval<RawSinkWrapper&>(), std::declval<const T&>()))>::value>>
245+ : std::true_type {};
246+
247+ template <typename T>
248+ inline constexpr bool has_custom_source_code_printer_v =
249+ HasCustomSourceCodePrinter<T>::value;
250+
251+ template <typename T>
252+ inline constexpr bool has_custom_printer_v =
253+ has_absl_stringify_v<T> || has_custom_source_code_printer_v<T>;
254+
255+ template <typename T, typename = std::enable_if_t <has_custom_printer_v<T>>>
256+ struct CustomPrinter {
257+ void PrintUserValue (const T& v, domain_implementor::RawSink out,
258+ domain_implementor::PrintMode mode) {
259+ RawSinkWrapper sink{out};
260+ if (mode == domain_implementor::PrintMode::kHumanReadable ) {
261+ // Prefer AbslStringify, fall back on source code printer.
262+ if constexpr (has_absl_stringify_v<T>) {
263+ absl::Format (out, " %v" , v);
264+ } else {
265+ FuzzTestPrintSourceCode (sink, v);
266+ }
267+ } else {
268+ // Prefer source code printer, fall back on AbslStringify.
269+ if constexpr (has_custom_source_code_printer_v<T>) {
270+ FuzzTestPrintSourceCode (sink, v);
271+ } else {
272+ absl::Format (out, " %v" , v);
273+ }
274+ }
275+ }
276+ };
277+
224278template <typename DomainT, typename ... Inner>
225279struct AggregatePrinter {
226280 const DomainT& domain;
@@ -230,12 +284,11 @@ struct AggregatePrinter {
230284 void PrintCorpusValue (const corpus_type_t <DomainT>& v,
231285 domain_implementor::RawSink out,
232286 domain_implementor::PrintMode mode) const {
233- if (mode == domain_implementor::PrintMode::kHumanReadable ) {
234- // In human-readable mode, prefer formatting with Abseil if possible.
235- if constexpr (has_absl_stringify_v<value_type_t <DomainT>>) {
236- absl::Format (out, " %v" , domain.GetValue (v));
237- return ;
238- }
287+ if constexpr (has_custom_printer_v<value_type_t <DomainT>>) {
288+ // Prefer the custom printer if there is one.
289+ CustomPrinter<value_type_t <DomainT>>{}.PrintUserValue (domain.GetValue (v),
290+ out, mode);
291+ return ;
239292 }
240293
241294 absl::Format (out, " %s" , type_name);
@@ -543,55 +596,10 @@ struct TimePrinter {
543596 }
544597};
545598
546- // A wrapper around `RawSink` that can be passed to the user's
547- // `FuzzTestPrintSourceCode` function, a FTADLE extension point for custom
548- // printers. This is so that the function can look more like `AbslStringify`,
549- // which is parameterized by the sink type and passes a pointer to the sink to
550- // `absl::Format()`.
551- struct RawSinkWrapper {
552- domain_implementor::RawSink& raw_sink;
553-
554- friend void AbslFormatFlush (RawSinkWrapper* absl_nonnull sink,
555- absl::string_view part) {
556- absl::Format (sink->raw_sink , " %s" , part);
557- }
558- };
559-
560- template <typename T, typename = void >
561- struct HasCustomSourceCodePrinter : std::false_type {};
562-
563- template <typename T>
564- struct HasCustomSourceCodePrinter <
565- T, std::enable_if_t <std::is_void<decltype (FuzzTestPrintSourceCode(
566- std::declval<RawSinkWrapper&>(), std::declval<const T&>()))>::value>>
567- : std::true_type {};
568-
569- template <typename T>
570- inline constexpr bool has_custom_source_code_printer_v =
571- HasCustomSourceCodePrinter<T>::value;
572-
573- template <typename T>
574- inline constexpr bool has_custom_printer_v =
575- has_absl_stringify_v<T> || has_custom_source_code_printer_v<T>;
576-
577599struct AutodetectAggregatePrinter {
578600 template <typename T>
579601 void PrintUserValue (const T& v, domain_implementor::RawSink out,
580602 domain_implementor::PrintMode mode) {
581- if (mode == domain_implementor::PrintMode::kHumanReadable ) {
582- // In human-readable mode, prefer formatting with Abseil if possible.
583- if constexpr (has_absl_stringify_v<T>) {
584- absl::Format (out, " %v" , v);
585- return ;
586- }
587- } else {
588- // In source-code mode, prefer custom source-code printer if possible.
589- if constexpr (has_custom_source_code_printer_v<T>) {
590- RawSinkWrapper sink{out};
591- FuzzTestPrintSourceCode (sink, v);
592- return ;
593- }
594- }
595603 std::tuple bound = DetectBindAggregate (v);
596604 const auto print_one = [&](auto I) {
597605 if (I > 0 ) absl::Format (out, " , " );
@@ -606,40 +614,11 @@ struct AutodetectAggregatePrinter {
606614 }
607615};
608616
609- template <typename T, typename = std::enable_if_t <has_custom_printer_v<T>>>
610- struct CustomPrinter {
611- void PrintUserValue (const T& v, domain_implementor::RawSink out,
612- domain_implementor::PrintMode mode) {
613- RawSinkWrapper sink{out};
614- if (mode == domain_implementor::PrintMode::kHumanReadable ) {
615- // Prefer AbslStringify, fall back on source code printer.
616- if constexpr (has_absl_stringify_v<T>) {
617- absl::Format (out, " %v" , v);
618- } else {
619- FuzzTestPrintSourceCode (sink, v);
620- }
621- } else {
622- // Prefer source code printer, fall back on AbslStringify.
623- if constexpr (has_custom_source_code_printer_v<T>) {
624- FuzzTestPrintSourceCode (sink, v);
625- } else {
626- absl::Format (out, " %v" , v);
627- }
628- }
629- }
630- };
631-
632617struct UnknownPrinter {
633618 template <typename T>
634619 void PrintUserValue (const T& v, domain_implementor::RawSink out,
635620 domain_implementor::PrintMode mode) {
636621 if (mode == domain_implementor::PrintMode::kHumanReadable ) {
637- // Try formatting with Abseil. We can't guarantee a good source code
638- // result, but it should be ok for human readable.
639- if constexpr (has_absl_stringify_v<T>) {
640- absl::Format (out, " %v" , v);
641- return ;
642- }
643622 // Some standard types have operator<<.
644623 if constexpr (std::is_scalar_v<T> || is_std_complex_v<T>) {
645624 absl::Format (out, " %s" , absl::FormatStreamed (v));
@@ -654,8 +633,8 @@ template <typename T>
654633decltype (auto ) AutodetectTypePrinter() {
655634 // The order of these checks somewhat matters. Most of the concrete types have
656635 // AbslStringify, so they should come first not to be captured by the custom
657- // printer case. The aggregate case is also more specific than the custom
658- // printer case, so it needs to come before .
636+ // printer case. The aggregate case comes after the custom case so that the
637+ // user can override the default aggregate printer .
659638 if constexpr (is_protocol_buffer_enum_v<T>) {
660639 return ProtobufEnumPrinter<const google::protobuf::EnumDescriptor*>{
661640 google::protobuf::GetEnumDescriptor<T>()};
@@ -676,10 +655,10 @@ decltype(auto) AutodetectTypePrinter() {
676655 return DurationPrinter{};
677656 } else if constexpr (std::is_same_v<T, absl::Time>) {
678657 return TimePrinter{};
679- } else if constexpr (is_bindable_aggregate_v<T>) {
680- return AutodetectAggregatePrinter{};
681658 } else if constexpr (has_custom_printer_v<T>) {
682659 return CustomPrinter<T>{};
660+ } else if constexpr (is_bindable_aggregate_v<T>) {
661+ return AutodetectAggregatePrinter{};
683662 } else {
684663 return UnknownPrinter{};
685664 }
0 commit comments