Skip to content

Latest commit

 

History

History
374 lines (311 loc) · 10.2 KB

236.md

File metadata and controls

374 lines (311 loc) · 10.2 KB
Info

Example

#include <cstdint>
#include <cstdio>
#include <utility>

struct trade {
  [[no_unique_address]] double price{42.};
  [[no_unique_address]] std::size_t size{1'000};
};

int main() {
  constexpr auto t = trade{};
  __builtin_dump_struct(std::addressof(t), std::addressof(std::printf));
}
const struct trade {
double price : 42.000000
std::size_t size : 1000
}

https://godbolt.org/z/GThTK3T46

Puzzle

  • Can you implement to_tuple_with_names which returns named fields based on __builtin_dump_struct input?
template<class T>
struct named {
  T value{};
  std::string_view name{};
};

template<class T> auto to_tuple_with_names(const T& t); // TODO

int main() {
  using namespace boost::ut;

  "to tuple with names"_test = [] {
    using std::literals::string_view_literals::operator""sv;

    should("be empty for empty struct") = [] {
      struct empty { };

      const auto & t = to_tuple_with_names(empty{});

      expect(0_u == std::tuple_size_v<std::remove_cvref_t<decltype(t)>>);
    };

    should("get value and names for struct with single field") = [] {
      struct trade {
        std::int32_t price{42};
      };

      const auto & t = to_tuple_with_names(trade{});

      expect(1_u == std::tuple_size_v<std::remove_cvref_t<decltype(t)>>);
      expect(42_i == std::get<0>(t).value and "price" == std::get<0>(t).name);
    };

    should("get value and names for struct with multiple fields") = [] {
      struct trade {
        std::int32_t price{42};
        std::uint32_t quantity{1'000u};
      };

      const auto & t = to_tuple_with_names(trade{});

      expect(2_u == std::tuple_size_v<std::remove_cvref_t<decltype(t)>>);
      expect(42_i == std::get<0>(t).value and "price" == std::get<0>(t).name);
      expect(1'000_u == std::get<1>(t).value and "quantity" == std::get<1>(t).name);
    };
  };
}

https://godbolt.org/z/7Ej67n513

Solutions

struct any_type {
    template<typename T> constexpr operator T() const;
};

template<class T, class ... Args>
constexpr bool is_braces_constructible = requires { T{std::declval<Args>()...};};

char buffer[100];
std::stringstream desc;
int printf_toStream(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int count = vsprintf(buffer, fmt, args);
    va_end(args);
    desc << buffer;
    return count;
}

template<class T> auto to_tuple_with_names(const T& t) {
    desc = std::stringstream();
    __builtin_dump_struct(std::addressof(t), std::addressof(printf_toStream));

    std::vector<std::string> lines;
    boost::split(lines, desc.str(), boost::is_any_of("\n"));
    std::vector<std::string> names;
    for(uint32_t i=0; i<lines.size()-3; i++)
    {
        std::vector<std::string> fields;
        boost::split(fields, lines[i+1], boost::is_any_of(" "));
        names.push_back(fields[1]);
    }

    if constexpr( is_braces_constructible<T, any_type, any_type> ) {
        auto&& [p0, p1] = t;
        return std::make_tuple( named<decltype(p0)>{p0, names[0]}, named<decltype(p1)>{p1, names[1]});
    } else if constexpr( is_braces_constructible<T, any_type> ) {
        auto&& [p1] = t;
        return std::make_tuple( named<decltype(p1)>{p1, names[0]});
    } else {
        return std::make_tuple();
    }
}

https://godbolt.org/z/Tdx8j6hxq

struct any_type { template<class T> constexpr operator T(); };

template<class T>
auto to_tuple_with_names(const T& t) {
  static std::vector<std::string> names{};
  names = {};

  struct set {
    static auto ns(const char* str, ...) -> int {
      std::string type_name{str};
      if (type_name.contains(" : ")) {
        type_name = type_name.substr(type_name.find(' ') + 1);
        names.push_back(type_name.substr(0, type_name.find(':') - 1));
      }
      return {};
    }
  };

  __builtin_dump_struct(std::addressof(t), std::addressof(set::ns));

  if constexpr(requires { T{any_type{}, any_type{}}; }) {
    auto [p1, p2] = t;
    assert(2 == std::size(names));
    return std::tuple(named<decltype(p1)>{.value = p1, .name = names[0]}, named<decltype(p2)>{.value = p2, .name = names[1]});
  } else if constexpr(requires { T{any_type{}}; }) {
    auto [p1] = t;
    assert(1 == std::size(names));
    return std::tuple(named<decltype(p1)>{.value = p1, .name = names[0]});
  } else {
    assert(std::empty(names));
    return std::tuple{};
  }
}

https://godbolt.org/z/Gd6PbvTdW

struct universal_arg { template<typename T> constexpr operator T() const; };
template <class T>
concept one_field = requires { T{universal_arg{}}; };
template <class T>
concept two_fields = requires { T{universal_arg{}, universal_arg{}}; };

static std::vector<std::string> names;

int printf_to_buffer(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  char buffer[256];
  const auto ret = vsprintf(buffer, fmt, args);
  va_end(args);

  if (const auto v = std::string_view{buffer}; v.contains(':')) {
    if (const auto name_start = v.find(' '); name_start != std::string_view::npos) {
      const auto name_end = v.find(' ', name_start + 1);
      names.emplace_back(&v[name_start + 1], &v[name_end]);
    }
  }

  return ret;
}

template <class T> auto to_tuple_with_names(const T& t) {
  names.clear();
  __builtin_dump_struct(std::addressof(t), &printf_to_buffer);

  if constexpr (two_fields<T>) {
    auto &[f0, f1] = t;
    return std::tuple{named{f0, names[0]}, named{f1, names[1]}};
  } else if constexpr (one_field<T>) {
    auto &[f0] = t;
    return std::tuple{named{f0, names[0]}};
  } else {
    return std::tuple{};
  }
}

https://godbolt.org/z/dj84eThqn

template<typename T>
struct Reflector
{
    #define MAX_NUM_FIELDS 255
    struct Any{
        Any(int){}
        template<typename MT> constexpr operator MT(){return MT{};}
    };
    template<int N>
    static consteval bool Initializeable( )
    {
        return []<int ... Is > ( std::integer_sequence<int, Is...> const & )
        {
            return (requires{ T { Any(Is) ... };});
        }( std::make_integer_sequence<int,N>());
    }
    template<int N = MAX_NUM_FIELDS > //search down from MAX_NUM_FIELDS
    static consteval int NumFields()
    {
        if constexpr ( Initializeable<N>())
        {
            return N;
        } else
            return NumFields<N-1>();
    }
    template<int N = NumFields()>
    static auto ToTuple( auto && t )
    {
        #define ELEMENTS(z,n,text) BOOST_PP_COMMA_IF( n ) BOOST_PP_CAT(text,n)
        #define OP(r, I) BOOST_PP_DEC(I)
        #define PRED(r, I) BOOST_PP_NOT_EQUAL(I, 0)
        #define TUPLE_N( r,I ) \
        if constexpr ( N == I ) { auto [BOOST_PP_REPEAT(I, ELEMENTS, a)] = t;return std::make_tuple(BOOST_PP_REPEAT(I, ELEMENTS, a));}
        BOOST_PP_FOR(MAX_NUM_FIELDS, PRED, OP, TUPLE_N)
        if constexpr( N == 0 ) return std::make_tuple();
    }
};

template< typename T >
struct NameParser{
    static std::vector<std::string> names;
    static int parse(const char* format,... )
    {
        int n = 0;
        int p1= 0;
        int p2= 0;
        while (format[n]!='\0' && format[n+1]!='\0')
        {
            if( format[n] == ' ')
            {
                if( p1 == 0 )
                    p1 = n;
                else
                    p2 = n ;
            }
            if( format[n] ==':' && format[n+1] ==' ' )
            {
                auto & name = names.emplace_back(p2-p1-1,'0');
                for( int i = p1 + 1 ; i < p2; ++i )
                    name[i-p1 - 1] = format[i];
            }
            ++n;
        }
        return 0;
    }
};
template <typename T> std::vector<std::string> NameParser<T>::names = {};
template<typename T>
struct NamedValue
{
    NamedValue(std::string const & name, T const & value ): name(name),value(value){}
    std::string name;
    T value;
};
template<class T> auto to_tuple_with_names(const T& t)
{
    __builtin_dump_struct(std::addressof(t), &NameParser<T>::parse);
    auto tp = Reflector<T>::ToTuple(t);
    return [&]<int ... IS>( std::integer_sequence<int, IS...> const &  )
    {
        return std::make_tuple( NamedValue( NameParser<T>::names[IS], std::get<IS>(tp)) ... );
    }( std::make_integer_sequence<int,std::tuple_size_v<std::remove_cvref_t<decltype(tp)>>>() );
}

https://godbolt.org/z/xodvvjc3f

namespace detail {

template <typename...>
struct always_false : std::false_type {};

template <auto>
struct any_type {
    template<typename T> constexpr operator T() const;
};

template <class T, std::size_t... Ns>
constexpr bool has_n_members_impl(std::index_sequence<Ns...>) {
    return requires { T{any_type<Ns>{}...}; };
}

template <class T, std::size_t N>
constexpr bool has_n_members = has_n_members_impl<T>(std::make_index_sequence<N>{});

} // namespace detail

struct to_tuple_with_names_impl {
    inline static std::vector<std::string> names{};

    static int dump_struct_helper(const char* fmt, ...) {
        if (const std::string_view dump{fmt}; dump.contains(':')) {
            const auto name_begin = dump.find(' ') + 1;
            const auto name_end = dump.find(' ', name_begin);
            names.emplace_back(&dump[name_begin], &dump[name_end]);
        }
        return {};
    }

    template <typename T>
    auto operator()(const T& t) const {
        names.clear();
        __builtin_dump_struct(std::addressof(t), &dump_struct_helper);

        if constexpr (detail::has_n_members<T, 2>) {
            const auto& [a, b] = t;
            return std::tuple {
                named {a, names[0]},
                named {b, names[1]}
            };
        } else if constexpr (detail::has_n_members<T, 1>) {
            const auto& [a] = t;
            return std::tuple {
                named {a, names[0]}
            };
        } else if constexpr (detail::has_n_members<T, 0>) {
            return std::tuple{};
        } else {
            static_assert(detail::always_false<T>::value, "struct is too large");
        }
    }
};

template<class T> [[nodiscard]] auto to_tuple_with_names(const T& t) {
    return to_tuple_with_names_impl{}(t);
}

https://godbolt.org/z/bP4116dh4