Skip to content

Commit 9091dbd

Browse files
Merge pull request #3 from contour-terminal/improvements/template_for
Create template_for utility and small updates
2 parents cfea26a + cf7918f commit 9091dbd

File tree

3 files changed

+103
-19
lines changed

3 files changed

+103
-19
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
[bB]uild
2+
[bB]uild/**
13
compile_commands.json
24
.cache/
35
out/
Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <tuple>
1010
#include <type_traits>
1111
#include <utility>
12+
#include <vector>
1213

1314
#if __has_include(<source_location>)
1415
#include <source_location>
@@ -347,8 +348,35 @@ namespace detail
347348
#elif defined(_MSC_VER)
348349
#endif
349350

351+
template <typename... Ts, typename F>
352+
constexpr void enumerate_types(F&& f)
353+
{
354+
[&f]<auto... Is>(std::index_sequence<Is...>) {
355+
(f.template operator()<Ts, Is>(), ...);
356+
}(std::index_sequence_for<Ts...> {});
357+
}
358+
359+
template <auto... Xs, typename F>
360+
constexpr void for_values(F&& f)
361+
{
362+
(f.template operator()<Xs>(), ...);
363+
}
364+
365+
template <typename T>
366+
constexpr bool can_be_formatted = std::is_arithmetic_v<T> || std::is_convertible_v<T, std::string>;
367+
350368
} // namespace detail
351369

370+
template <auto B, auto E, typename F>
371+
constexpr void template_for(F&& f)
372+
{
373+
using t = std::common_type_t<decltype(B), decltype(E)>;
374+
375+
[&f]<auto... Xs>(std::integer_sequence<t, Xs...>) {
376+
detail::for_values<(B + Xs)...>(f);
377+
}(std::make_integer_sequence<t, E - B> {});
378+
}
379+
352380
template <auto P>
353381
requires(std::is_member_pointer_v<decltype(P)>)
354382
consteval std::string_view GetName()
@@ -392,30 +420,55 @@ consteval auto GetName()
392420
#endif
393421
}
394422

423+
template <typename Object, typename Callable>
424+
decltype(auto) CallOnMembers(Object const& object, Callable&& callable)
425+
{
426+
template_for<0, Reflection::CountMembers<Object>>(
427+
[&]<auto I>() { callable(Reflection::MemberNameOf<I, Object>, std::get<I>(Reflection::ToTuple(object))); });
428+
}
429+
395430
template <typename Object>
396431
std::string Inspect(Object const& object)
397432
{
398-
return [&]<size_t... I>(std::index_sequence<I...>) {
399-
std::string str;
400-
auto const onMember = [&str]<typename Name, typename Value>(Name&& name, Value&& value) {
401-
auto const InspectValue = [&str]<typename T>(T&& arg) {
402-
// clang-format off
403-
if constexpr (std::is_convertible_v<T, std::string>
433+
std::string str;
434+
auto const onMember = [&str]<typename Name, typename Value>(Name&& name, Value&& value) {
435+
auto const InspectValue = [&str]<typename T>(T&& arg) {
436+
// clang-format off
437+
if constexpr (std::is_convertible_v<T, std::string>
404438
|| std::is_convertible_v<T, std::string_view>
405439
|| std::is_convertible_v<T, char const*>) // clang-format on
406-
str += std::format("\"{}\"", arg);
407-
else
408-
str += std::format("{}", arg);
409-
};
410-
if (!str.empty())
411-
str += ' ';
412-
str += name;
413-
str += '=';
414-
InspectValue(value);
440+
{
441+
str += std::format("\"{}\"", arg);
442+
}
443+
else if constexpr (std::is_convertible_v<T, int>) // use std::formattable when available
444+
{
445+
str += std::format("{}", arg);
446+
}
447+
else
448+
{
449+
str += Inspect(arg);
450+
}
415451
};
416-
(onMember(MemberNameOf<I, Object>, std::get<I>(Reflection::ToTuple(object))), ...);
417-
return str;
418-
}(std::make_index_sequence<Reflection::CountMembers<Object>> {});
452+
if (!str.empty())
453+
str += ' ';
454+
str += name;
455+
str += '=';
456+
InspectValue(value);
457+
};
458+
459+
CallOnMembers(object, onMember);
460+
return str;
419461
}
420462

463+
template <typename Object>
464+
std::string Inspect(std::vector<Object> const& objects)
465+
{
466+
std::string str;
467+
for (auto const& object: objects)
468+
{
469+
str += Inspect(object);
470+
str += '\n';
471+
}
472+
return str;
473+
}
421474
} // namespace Reflection

test-reflection-cpp.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// SPDX-License-Identifier: Apache-2.0
2-
#include <reflection-cpp/Reflection.hpp>
2+
#include <reflection-cpp/reflection.hpp>
33

44
#include <catch2/catch_test_macros.hpp>
55

6+
#include <iostream>
67
#include <string>
78
#include <string_view>
89

@@ -13,6 +14,15 @@ struct Person
1314
int age;
1415
};
1516

17+
struct TestStruct
18+
{
19+
int a;
20+
float b;
21+
double c;
22+
std::string d;
23+
Person e;
24+
};
25+
1626
enum Color
1727
{
1828
Red,
@@ -39,3 +49,22 @@ TEST_CASE("core", "[reflection]")
3949
auto const result = Reflection::Inspect(p);
4050
CHECK(result == R"(name="John Doe" email="[email protected]" age=42)");
4151
}
52+
53+
TEST_CASE("vector", "[reflection]")
54+
{
55+
auto v = std::vector<Person> {};
56+
v.emplace_back("John Doe", "[email protected]", 42);
57+
v.emplace_back("John Doe", "[email protected]", 43);
58+
auto const result = Reflection::Inspect(v);
59+
CHECK(result == R"(name="John Doe" email="[email protected]" age=42
60+
name="John Doe" email="[email protected]" age=43
61+
)");
62+
//clang-format on
63+
}
64+
65+
TEST_CASE("nested", "[reflection]")
66+
{
67+
auto ts = TestStruct { 1, 2.0f, 3.0, "hello", { "John Doe", "[email protected]", 42 } };
68+
auto const result = Reflection::Inspect(ts);
69+
CHECK(result == R"(a=1 b=2 c=3 d="hello" e=name="John Doe" email="[email protected]" age=42)");
70+
}

0 commit comments

Comments
 (0)