#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/util.h"
namespace cpp17 {
template<typename T>
std::optional<std::string> StringifyFlatbufferValue(
T &&val, const std::string &indent = "");
namespace detail {
template<typename FBS, typename = void>
struct is_flatbuffers_table_or_struct : std::false_type {};
template<typename FBS>
struct is_flatbuffers_table_or_struct<FBS, std::void_t<typename FBS::Traits>>
: std::true_type {};
template<typename FBS>
inline constexpr bool is_flatbuffers_table_or_struct_v =
is_flatbuffers_table_or_struct<FBS>::value;
template<typename T> struct is_flatbuffers_vector : std::false_type {};
template<typename T>
struct is_flatbuffers_vector<flatbuffers::Vector<T>> : std::true_type {};
template<typename T>
inline constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector<T>::value;
template<size_t Index, typename FBS>
std::string AddStringifiedField(const FBS &fbs, const std::string &indent) {
auto value_string =
StringifyFlatbufferValue(fbs.template get_field<Index>(), indent);
if (!value_string) { return ""; }
return indent + FBS::Traits::field_names[Index] + " = " + *value_string +
"\n";
}
template<typename FBS, size_t... Indexes>
std::string StringifyTableOrStructImpl(const FBS &fbs,
const std::string &indent,
std::index_sequence<Indexes...>) {
return (AddStringifiedField<Indexes>(fbs, indent) + ...);
}
template<typename FBS>
std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) {
(void)fbs;
(void)indent;
static constexpr size_t field_count = FBS::Traits::fields_number;
std::string out;
if constexpr (field_count > 0) {
out = std::string(FBS::Traits::fully_qualified_name) + "{\n" +
StringifyTableOrStructImpl(fbs, indent + " ",
std::make_index_sequence<field_count>{}) +
indent + '}';
}
return out;
}
template<typename T>
std::string StringifyVector(const flatbuffers::Vector<T> &vec,
const std::string &indent) {
const auto prologue = indent + std::string(" ");
const auto epilogue = std::string(",\n");
std::string text;
text += "[\n";
for (auto it = vec.cbegin(), end = vec.cend(); it != end; ++it) {
text += prologue;
text += StringifyFlatbufferValue(*it).value_or("(field absent)");
text += epilogue;
}
if (vec.cbegin() != vec.cend()) {
text.resize(text.size() - epilogue.size());
}
text += '\n' + indent + ']';
return text;
}
template<typename T> std::string StringifyArithmeticType(T val) {
return flatbuffers::NumToString(val);
}
}
template<typename T>
std::optional<std::string> StringifyFlatbufferValue(T &&val,
const std::string &indent) {
(void)indent;
constexpr bool is_pointer = std::is_pointer_v<std::remove_reference_t<T>>;
if constexpr (is_pointer) {
if (val == nullptr) return std::nullopt; }
using decayed =
std::decay_t<std::remove_pointer_t<std::remove_reference_t<T>>>;
if constexpr (detail::is_flatbuffers_table_or_struct_v<decayed>) {
if constexpr (is_pointer)
return detail::StringifyTableOrStruct(*val, indent);
else
return detail::StringifyTableOrStruct(val, indent);
}
else if constexpr (std::is_same_v<decayed, int8_t> ||
std::is_same_v<decayed, uint8_t>) {
return detail::StringifyArithmeticType(static_cast<int>(val));
}
else if constexpr (std::is_enum_v<decayed>) {
return StringifyFlatbufferValue(
static_cast<std::underlying_type_t<decayed>>(val), indent);
}
else if constexpr (std::is_arithmetic_v<decayed>) {
return detail::StringifyArithmeticType(val);
}
else if constexpr (std::is_same_v<decayed, flatbuffers::String>) {
return '"' + val->str() + '"';
}
else if constexpr (detail::is_flatbuffers_vector_v<decayed>) {
return detail::StringifyVector(*val, indent);
}
else if constexpr (std::is_same_v<decayed, void>) {
return std::nullopt;
}
else {
static_assert(sizeof(T) != sizeof(T),
"Do not know how to format this type T (the compiler error "
"should tell you nearby what T is).");
}
}
}