Info
-
Did you know that static reflection proposal for C++2X can reflect functions?
Example
struct foo {
auto api(int i) -> void;
};
auto main() -> int {
namespace meta = std::experimental::reflect;
std::cout << meta::get_size_v<meta::get_member_functions_t<reflexpr(foo)>>; // prints 1
std::cout << meta::get_name_v<meta::get_element_t<0, meta::get_member_functions_t<reflexpr(foo)>>>; // prints api
std::cout << meta::get_name_v<meta::get_element_t<0, meta::get_parameters_t<meta::get_element_t<0, meta::get_member_functions_t<reflexpr(foo)>>>>>; // prints i
}
Puzzle
- Can you implement to_string which reflects member functions for given type?
struct empty {};
struct foo {
auto api(int i);
auto api(int i, double d);
};
template<class T> auto to_string(); // TODO
int main() {
using namespace boost::ut;
using std::literals::string_literals::operator""s;
"reflexpr funtions"_test = [] {
expect("empty{}"s == to_string<empty>());
expect("foo{api(int i);api(int i, double d);}"s == to_string<foo>());
};
}
Solutions
template <class...>
struct stringify_parameters;
template <class PList, auto... Is>
struct stringify_parameters<PList, std::index_sequence<Is...>> {
auto operator()() const {
const auto type_and_name = [] <class P> {
return std::string{meta::get_name_v<meta::get_type_t<P>>} + ' ' + meta::get_name_v<P> + ", ";
};
std::string s{("" + ... + type_and_name.template operator()<meta::get_element_t<Is, PList>>())};
if constexpr (sizeof...(Is) > 0) {
s.pop_back();
s.pop_back();
}
return s;
}
};
template <class... Ts>
struct stringify_member_funcs {
auto operator()() const {
const auto member_func = [] <class T> {
using Ps = meta::get_parameters_t<T>;
// why doesn't unpack_sequence_t work on Ps?
return std::string{meta::get_name_v<T>} + '('
+ stringify_parameters<Ps, std::make_index_sequence<meta::get_size_v<Ps>>>{}()
+ ");";
};
return ("" + ... + member_func.template operator()<Ts>());
}
};
template <class T> auto to_string() {
using type = meta::get_aliased_t<reflexpr(T)>;
using member_functions_t = meta::get_member_functions_t<type>;
return std::string{meta::get_name_v<type>} + '{'
+ meta::unpack_sequence_t<stringify_member_funcs, member_functions_t>{}() + '}';
}
template<class T_> auto to_string() {
namespace meta = std::experimental::reflect;
using T = meta::get_aliased_t<reflexpr(T_)>;
std::stringstream str{};
str << meta::get_name_v<T> << '{';
[&]<class... Ts>(std::tuple<Ts...>) {
const auto f = [&]<class T> {
str << std::experimental::reflect::get_name_v<T> << '(';
[&]<class TParams, auto... Ns>(TParams, std::index_sequence<Ns...>) {
((str << meta::get_name_v<meta::get_type_t<meta::get_element_t<Ns, TParams>>> << ' ' << meta::get_name_v<meta::get_element_t<Ns, TParams>> << ", "), ...);
str.seekp(-2, std::ios_base::end);
}(meta::get_parameters_t<T>{}, std::make_index_sequence<meta::get_size_v<meta::get_parameters_t<T>>>{});
str << ");";
};
(f.template operator()<Ts>(), ...);
}(meta::unpack_sequence_t<std::tuple, meta::get_member_functions_t<T>>{});
str << '}';
return str.str();
}
namespace detail {
template <meta::Object T>
[[nodiscard]] auto get_name() -> std::string {
return meta::get_name_v<T>;
}
template <meta::Typed T>
[[nodiscard]] auto get_type() -> std::string {
return meta::get_name_v<meta::get_type_t<T>>;
}
template <meta::FunctionParameter T>
[[nodiscard]] auto get_parameter_representation() -> std::string {
return get_type<T>() + ' ' + get_name<T>();
}
template <meta::Function T>
[[nodiscard]] auto get_parameter_representations() -> std::vector<std::string> {
// BUG: unpack_sequence_t doesn't work. If it did, this could be unified
// with get_member_representations()
using parameter_list_t = meta::get_parameters_t<T>;
return
[]<auto... Is>(std::index_sequence<Is...>)->std::vector<std::string> {
return {get_parameter_representation<
meta::get_element_t<Is, parameter_list_t>>()...};
}
(std::make_index_sequence<meta::get_size_v<parameter_list_t>>{});
}
template <meta::Function T>
[[nodiscard]] auto get_function_representation() -> std::string {
return get_name<T>() + '(' +
boost::algorithm::join(get_parameter_representations<T>(), ", ") +
')';
}
template <meta::Class T>
[[nodiscard]] auto get_member_representations() -> std::vector<std::string> {
using sequence_t = meta::unpack_sequence_t<boost::mp11::mp_list,
meta::get_member_functions_t<T>>;
return []<typename... Ts>(boost::mp11::mp_list<Ts...>)
->std::vector<std::string> {
return {get_function_representation<Ts>()...};
}
(sequence_t{}); // TODO: avoid instantiation?
}
template <meta::Class T>
[[nodiscard]] auto get_object_representation() -> std::string {
const auto members = get_member_representations<T>();
return get_name<T>() + '{' +
std::accumulate(std::cbegin(members), std::cend(members),
std::string{},
[](auto acc, const auto& elem) {
acc += elem + ';';
return acc;
}) +
'}';
}
} // namespace detail
template <class T>
auto to_string() {
return detail::get_object_representation<
meta::get_aliased_t<reflexpr(T)>>();
}
template <typename TParams>
auto parameters_to_string() {
return []<auto... Is>(std::index_sequence<Is...>) {
const auto stringify = []<typename TParam> {
return std::format("{} {}, ",
meta::get_name_v<meta::get_type_t<TParam>>,
meta::get_name_v<TParam>);
};
auto s = std::string{("" + ... + stringify.template operator()<meta::get_element_t<Is, TParams>>())};
if constexpr (sizeof...(Is) > 0) {
s.erase(std::prev(std::cend(s), 2), std::cend(s));
}
return s;
}(std::make_index_sequence<meta::get_size_v<TParams>>{});
};
template<typename... TFuncs>
struct member_functions_to_string {
auto operator()() const {
const auto stringify = []<typename TFunc> {
return std::format("{}({});",
meta::get_name_v<TFunc>,
parameters_to_string<meta::get_parameters_t<TFunc>>());
};
return ("" + ... + stringify.template operator()<TFuncs>());
}
};
template <typename T>
auto class_name() {
using type = meta::get_aliased_t<reflexpr(T)>;
using member_functions_t = meta::get_member_functions_t<type>;
return std::format("{}{{{}}}",
meta::get_name_v<type>,
meta::unpack_sequence_t<member_functions_to_string, member_functions_t>{}());
}
template<class T> auto to_string() {
return class_name<T>();
template <class T>
decltype(auto) unpack(auto &&initial, auto f) {
return [&]<class... Ts>(std::tuple<Ts...>)->decltype(auto) {
return (initial + ... + f.template operator()<Ts>());
}
(meta::unpack_sequence_t<std::tuple, T>{});
}
template <class T, template <class> class pack_t>
std::string format_name_and_sequence(const auto &fmt, const auto &f) {
return std::format(fmt, meta::get_name_v<T>, unpack<pack_t<T>>("", f));
}
template <class T>
auto to_string() {
const auto parameter = [front = true]<class TParameter> mutable {
return std::format("{}{} {}", front ? (front = false, "") : ", ",
meta::get_name_v<meta::get_type_t<TParameter>>,
meta::get_name_v<TParameter>);
};
const auto member_function = [=]<class TMemberFunction> {
return format_name_and_sequence<TMemberFunction, meta::get_parameters_t>(
"{}({});", parameter);
};
using class_t = meta::get_aliased_t<reflexpr(T)>;
return format_name_and_sequence<class_t, meta::get_member_functions_t>(
"{}{{{}}}", member_function);
}
template<class Param> auto param_to_string() {
return std::string{meta::get_name_v<meta::get_type_t<Param>>} + " " + meta::get_name_v<Param>;
}
template<class Params> auto params_to_string() {
return []<std::size_t... I>(std::index_sequence<I...>){
return (((I == 0 ? "" : ", ") + param_to_string<meta::get_element_t<I,Params>>()) + ... + "");
}(std::make_index_sequence<meta::get_size_v<Params>>{});
}
template<class MemFn> auto memfn_to_string() {
return std::string{meta::get_name_v<MemFn>} + "(" + params_to_string<meta::get_parameters_t<MemFn>>() + ");";
}
template<class MemFns> auto memfns_to_string() {
return []<std::size_t... I>(std::index_sequence<I...>) {
return (memfn_to_string<meta::get_element_t<I, MemFns>>() + ... + "");
}(std::make_index_sequence<meta::get_size_v<MemFns>>{});
}
template<class T> auto to_string() {
using Aliased = meta::get_aliased_t<reflexpr(T)>;
return std::string{meta::get_name_v<Aliased>} + "{" + memfns_to_string<meta::get_member_functions_t<Aliased>>() + "}";
}
template<class ...Ts>
struct FunArgString {
auto operator()() {
std::string str = ( std::string{meta::get_name_v<meta::get_type_t<Ts>>} + std::string{" "} + std::string{meta::get_name_v<Ts>} +", " + ... + std::string{});
if(str.size() > 0)
str = str.substr(0,str.size()-2);
return str;
}
};
template<class ...T>
struct MemFunString {
auto operator()(char sep) {
auto wrap = []<typename P>(P) { return "(" + meta::unpack_sequence_t<FunArgString, meta::get_parameters_t<P>>{}() +")"; };
std::string str = ( (std::string{meta::get_name_v<T>}+ wrap(T{}) + std::string{sep}) + ... +std::string{});
return str;
}
};
template<class T> auto to_string() {
using meta_T = meta::get_aliased_t<reflexpr(T)>;
using meta_T_funcs = meta::get_member_functions_t<meta_T>;
std::string ret = meta::get_name_v<meta_T>;
ret += "{";
ret += meta::unpack_sequence_t<MemFunString, meta::get_member_functions_t<meta_T>>{}(';');
ret += "}";
return ret;
}