blob: 94cb4fc120f83401d08d1783707ed38a89abfe4c [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_INTERNAL_DIAGNOSTICS_PRINTF_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_INTERNAL_DIAGNOSTICS_PRINTF_H_
#include <inttypes.h>
#include <string_view>
#include <tuple>
#include <type_traits>
#include "const-string.h"
namespace elfldltl {
template <typename T>
struct FileOffset;
template <typename T>
struct FileAddress;
class SymbolName;
namespace internal {
// This only exists to be specialized. The interface is shown here.
template <typename T>
struct PrintfType {
// The default template also handles other unsigned types that are the
// same size as one of the uintNN_t types but a different type, which
// just get widened to uint64_t.
static_assert(std::is_unsigned_v<T>, "missing specialization");
// This is a ConstString of a printf format string fragment.
static constexpr auto kFormat = ConstString(" %" PRIu64);
// This is a function of T that returns a std::tuple<...> of the arguments to
// pass to printf corresponding to the kFormat string.
static constexpr auto Arguments(uint64_t arg) { return std::make_tuple(arg); }
};
template <typename T>
struct PrintfType<T&> : public PrintfType<T> {};
template <typename T>
struct PrintfType<const T&> : public PrintfType<T> {};
template <typename T>
struct PrintfType<T&&> : public PrintfType<T> {};
template <>
struct PrintfType<uint8_t> {
static constexpr auto kFormat = ConstString(" %" PRIu8);
static constexpr auto Arguments(uint8_t arg) { return std::make_tuple(arg); }
};
template <>
struct PrintfType<uint16_t> {
static constexpr auto kFormat = ConstString(" %" PRIu16);
static constexpr auto Arguments(uint16_t arg) { return std::make_tuple(arg); }
};
template <>
struct PrintfType<uint32_t> {
static constexpr auto kFormat = ConstString(" %" PRIu32);
static constexpr auto Arguments(uint32_t arg) { return std::make_tuple(arg); }
};
template <typename T, size_t N>
struct Map {
using Type = T;
ConstString<N> string;
};
template <typename T, size_t N>
constexpr auto FormatFor(const char (&string)[N]) {
return Map<T, N - 1>{string};
}
template <typename T, class First, class... Rest>
constexpr auto Pick(First first, Rest... rest) {
if constexpr (std::is_same_v<T, typename First::Type>) {
return first.string;
} else {
static_assert(sizeof...(Rest) > 0, "missing type?");
return Pick<T>(rest...);
}
}
template <typename T>
constexpr auto PrintfHexFormatStringForType() {
return ConstString(" %#") + Pick<T>( //
FormatFor<uint8_t>(PRIx8), //
FormatFor<uint16_t>(PRIx16), //
FormatFor<uint32_t>(PRIx32), //
FormatFor<uint64_t>(PRIx64), //
FormatFor<unsigned char>("hhx"), //
FormatFor<unsigned short int>("hx"), //
FormatFor<unsigned int>("x"), //
FormatFor<unsigned long int>("lx"), //
FormatFor<unsigned long long int>("llx"));
}
// This handles string literals. It could fold them into the format
// string, but that would require doubling any '%' inside.
template <size_t N>
struct PrintfType<const char (&)[N]> {
static constexpr auto kFormat = ConstString("%s");
static constexpr auto Arguments(const char (&str)[N]) { return std::forward_as_tuple(str); }
};
template <size_t Len>
struct PrintfType<ConstString<Len>> {
static constexpr auto kFormat = ConstString("%s");
static constexpr auto Arguments(const ConstString<Len>& str) {
return std::make_tuple(str.c_str());
}
};
template <>
struct PrintfType<const char*> {
static constexpr auto kFormat = ConstString("%s");
static constexpr auto Arguments(const char* str) { return std::make_tuple(str); }
};
template <>
struct PrintfType<std::string_view> {
static constexpr auto kFormat = ConstString("%.*s");
static constexpr auto Arguments(std::string_view str) {
return std::make_tuple(static_cast<int>(str.size()), str.data());
}
};
template <>
struct PrintfType<SymbolName> : public PrintfType<std::string_view> {};
template <typename T>
struct PrintfType<FileOffset<T>> {
static constexpr auto kFormat =
ConstString(" at file offset") + PrintfHexFormatStringForType<T>();
static constexpr auto Arguments(FileOffset<T> arg) { return std::make_tuple(*arg); }
};
template <typename T>
struct PrintfType<FileAddress<T>> {
static constexpr auto kFormat =
ConstString(" at relative address") + PrintfHexFormatStringForType<T>();
static constexpr auto Arguments(FileAddress<T> arg) { return std::make_tuple(*arg); }
};
// This concatenates them all together in a mandatory constexpr context so the
// whole format string becomes effectively a single string literal.
template <typename... T>
inline constexpr auto kPrintfFormat = (PrintfType<T>::kFormat + ...);
// Specialize the empty case.
template <>
inline constexpr auto kPrintfFormat<> = ConstString("");
// Calls printer("format string", ...) with arguments corresponding to
// prefix..., args... (each prefix argument and each later argument might
// produce multiple arguments to printer).
template <typename Printer, typename... Prefix, typename... Args>
constexpr void Printf(Printer&& printer, std::tuple<Prefix...> prefix, Args&&... args) {
constexpr auto printer_args = [](auto&&... args) {
constexpr auto kFormat = kPrintfFormat<decltype(args)...>.c_str();
constexpr auto arg_tuple = [](auto&& arg) {
using T = decltype(arg);
return PrintfType<T>::Arguments(std::forward<T>(arg));
};
return std::tuple_cat(std::make_tuple(kFormat),
arg_tuple(std::forward<decltype(args)>(args))...);
};
std::apply(
std::forward<Printer>(printer),
std::apply(printer_args, std::tuple_cat(std::move(prefix),
std::forward_as_tuple(std::forward<Args>(args)...))));
}
} // namespace internal
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_INTERNAL_DIAGNOSTICS_PRINTF_H_