Skip to content

Instantly share code, notes, and snippets.

@bkietz
Last active June 17, 2021 17:02
Show Gist options
  • Save bkietz/7899f477e86df49f21ab17201c518d74 to your computer and use it in GitHub Desktop.
Save bkietz/7899f477e86df49f21ab17201c518d74 to your computer and use it in GitHub Desktop.
Simple reflection for data members, see https://issues.apache.org/jira/browse/ARROW-13097
/// Distributed under the Boost Software License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
#include <tuple>
#include <cassert>
#include "reflection.h"
struct Person {
std::string name;
int age;
};
int main() {
Person genos{"Genos", 19};
Person kuseno{"Kuseno", 45};
// no need to own/modify struct, just enumerate properties
using properties =
std::tuple<arrow::util::DataMember<Person, int, &Person::age>,
arrow::util::DataMember<Person, std::string, &Person::name>>;
// relies on each property supporting operator==
assert(arrow::util::Equals<properties>(genos, genos));
assert(!arrow::util::Equals<properties>(kuseno, genos));
// relies on an unqualified call to GenericToString(each property)
assert(arrow::util::ToString<properties>(genos) == "{age: 19, name: Genos}");
assert(arrow::util::ToString<properties>(kuseno) ==
"{age: 45, name: Kuseno}");
}
/// Distributed under the Boost Software License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
#include <array>
#include <string>
#include <utility>
#include <tuple>
namespace arrow {
namespace util {
namespace detail {
#ifdef _MSC_VER
#define ARROW_PRETTY_FUNCTION __FUNCSIG__
#else
#define ARROW_PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif
template <typename T>
const char* raw() {
return ARROW_PRETTY_FUNCTION;
}
template <typename T>
size_t raw_sizeof() {
return sizeof(ARROW_PRETTY_FUNCTION);
}
#undef ARROW_PRETTY_FUNCTION
constexpr bool starts_with(char const* haystack, char const* needle) {
return needle[0] == '\0' ||
(haystack[0] == needle[0] && starts_with(haystack + 1, needle + 1));
}
constexpr size_t search(char const* haystack, char const* needle) {
return haystack[0] == '\0' || starts_with(haystack, needle)
? 0
: search(haystack + 1, needle) + 1;
}
const size_t typename_prefix = search(raw<double>(), "double");
template <typename T>
size_t struct_class_prefix() {
#ifdef _MSC_VER
return starts_with(raw<T>() + typename_prefix, "struct ")
? 7
: starts_with(raw<T>() + typename_prefix, "class ") ? 6 : 0;
#else
return 0;
#endif
}
template <typename T>
size_t typename_length() {
// raw_sizeof<T>() - raw_sizeof<double>() ==
// (length of T's name) - strlen("double")
// (length of T's name) ==
// raw_sizeof<T>() - raw_sizeof<double>() + strlen("double")
return raw_sizeof<T>() - struct_class_prefix<T>() - raw_sizeof<double>() + 6;
}
template <typename T>
const char* typename_begin() {
return raw<T>() + struct_class_prefix<T>() + typename_prefix;
}
template <class Class, typename Type, Type Class::*Ptr>
struct member_pointer_constant {
static constexpr auto value = Ptr;
};
struct HasCrib {
double crib;
using crib_constant =
member_pointer_constant<HasCrib, double, &HasCrib::crib>;
};
const size_t membername_boilerplate =
search(raw<HasCrib::crib_constant>(), "crib") -
typename_length<HasCrib>() * 2 - typename_length<double>();
template <class Class, typename Type, Type Class::*Ptr>
size_t membername_prefix() {
return membername_boilerplate + typename_length<Class>() * 2 +
typename_length<Type>();
}
const size_t membername_suffix =
raw_sizeof<HasCrib::crib_constant>() -
membername_prefix<HasCrib, double, &HasCrib::crib>() -
4; // == strlen("crib")
template <class Class, typename Type, Type Class::*Ptr>
const char* membername_begin() {
return raw<member_pointer_constant<Class, Type, Ptr>>() +
membername_prefix<Class, Type, Ptr>();
}
template <class Class, typename Type, Type Class::*Ptr>
size_t membername_length() {
return raw_sizeof<member_pointer_constant<Class, Type, Ptr>>() -
membername_prefix<Class, Type, Ptr>() - membername_suffix;
}
} // namespace detail
template <typename T>
std::string nameof(bool strip_namespace = false) {
std::string name{detail::typename_begin<T>(), detail::typename_length<T>()};
if (strip_namespace) {
auto i = name.find_last_of("::");
if (i != std::string::npos) {
name = name.substr(i + 1);
}
}
return name;
}
template <size_t...>
struct index_sequence {};
template <size_t N, size_t Head = N, size_t... Tail>
struct make_index_sequence_impl;
template <size_t N>
using make_index_sequence = typename make_index_sequence_impl<N>::type;
template <typename... T>
using index_sequence_for = make_index_sequence<sizeof...(T)>;
template <size_t N, size_t... I>
struct make_index_sequence_impl<N, 0, I...> {
using type = index_sequence<I...>;
};
template <size_t N, size_t H, size_t... I>
struct make_index_sequence_impl
: make_index_sequence_impl<N, H - 1, H - 1, I...> {};
static_assert(std::is_base_of<index_sequence<>, make_index_sequence<0>>::value,
"");
static_assert(
std::is_base_of<index_sequence<0, 1, 2>, make_index_sequence<3>>::value,
"");
template <typename Props, typename Fn, size_t... I>
void for_each_property_impl(Fn&& fn, const index_sequence<I...>&) {
struct {
} dummy;
std::make_tuple((std::forward<Fn>(fn)(std::get<I>(Props{}), I), dummy)...);
}
template <typename Props, typename Fn>
void ForEachProperty(Fn&& fn) {
for_each_property_impl<Props>(
std::forward<Fn>(fn),
make_index_sequence<std::tuple_size<Props>::value>{});
}
template <typename Class, typename Type, Type Class::*Ptr>
struct DataMember {
using type = Type;
static constexpr auto ptr = Ptr;
static constexpr const type& get(const Class& obj) { return obj.*ptr; }
static void set(Class* obj, type value) { (*obj).*ptr = std::move(value); }
static std::string name() {
return std::string(detail::membername_begin<Class, Type, Ptr>(),
detail::membername_length<Class, Type, Ptr>());
}
};
template <typename Class>
struct EqualsImpl {
template <typename Property>
void operator()(const Property& prop, ...) const {
*out &= prop.get(l) == prop.get(r);
}
const Class& l;
const Class& r;
bool* out;
};
template <typename Properties, typename Class>
bool Equals(const Class& l, const Class& r) {
bool out = true;
ForEachProperty<Properties>(EqualsImpl<Class>{l, r, &out});
return out;
}
const std::string& GenericToString(const std::string& str) { return str; }
std::string GenericToString(int i) { return std::to_string(i); }
template <typename Class, int NumProperties>
struct ToStringImpl {
template <typename Property>
void operator()(const Property& prop, size_t i) {
members[i] = prop.name() + ": " + GenericToString(prop.get(obj));
}
std::string Finish() {
std::string out = "{";
for (auto&& member : members) {
out += member + ", ";
}
out.resize(out.size() - 1);
out.back() = '}';
return out;
}
const Class& obj;
std::array<std::string, NumProperties> members;
};
template <typename Properties, typename Class>
std::string ToString(const Class& obj) {
arrow::util::ToStringImpl<Class, std::tuple_size<Properties>::value> impl{
obj};
arrow::util::ForEachProperty<Properties>(impl);
return impl.Finish();
}
} // namespace util
} // namespace arrow
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment