Skip to content

Commit faca0e2

Browse files
committed
refactor(core): centralize traits, remove duplicates and fix tap collision between print and inspect
1 parent 50da754 commit faca0e2

File tree

3 files changed

+634
-767
lines changed

3 files changed

+634
-767
lines changed

include/vix/inspect/Inspect.hpp

Lines changed: 5 additions & 344 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <utility>
5050
#include <variant>
5151
#include <vector>
52+
#include <vix/meta/traits.hpp>
5253

5354
#if defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
5455
#include <expected>
@@ -70,11 +71,11 @@ namespace vix
7071
struct inspect_options;
7172

7273
/// @brief Primary inspector template — specialize to teach inspect about your type.
73-
template <typename T, typename = void>
74+
template <typename T, typename>
7475
struct inspector;
7576

7677
/// @brief Field map template — specialize to register struct fields for reflection.
77-
template <typename T, typename = void>
78+
template <typename T, typename>
7879
struct field_map;
7980

8081
namespace detail
@@ -166,346 +167,6 @@ namespace vix
166167

167168
} // namespace demangle
168169

169-
namespace traits
170-
{
171-
172-
// Streamable via operator<<
173-
template <typename T, typename = void>
174-
struct is_ostreamable : std::false_type
175-
{
176-
};
177-
template <typename T>
178-
struct is_ostreamable<T,
179-
std::void_t<decltype(std::declval<std::ostream &>() << std::declval<const T &>())>>
180-
: std::true_type
181-
{
182-
};
183-
template <typename T>
184-
inline constexpr bool is_ostreamable_v = is_ostreamable<T>::value;
185-
186-
// String-like (atomic, not iterable)
187-
template <typename T>
188-
struct is_string_like : std::false_type
189-
{
190-
};
191-
template <>
192-
struct is_string_like<std::string> : std::true_type
193-
{
194-
};
195-
template <>
196-
struct is_string_like<std::string_view> : std::true_type
197-
{
198-
};
199-
template <>
200-
struct is_string_like<const char *> : std::true_type
201-
{
202-
};
203-
template <>
204-
struct is_string_like<char *> : std::true_type
205-
{
206-
};
207-
template <>
208-
struct is_string_like<std::wstring> : std::true_type
209-
{
210-
};
211-
template <>
212-
struct is_string_like<std::wstring_view> : std::true_type
213-
{
214-
};
215-
template <>
216-
struct is_string_like<const wchar_t *> : std::true_type
217-
{
218-
};
219-
template <std::size_t N>
220-
struct is_string_like<char[N]> : std::true_type
221-
{
222-
};
223-
template <std::size_t N>
224-
struct is_string_like<const char[N]> : std::true_type
225-
{
226-
};
227-
template <typename T>
228-
inline constexpr bool is_string_like_v = is_string_like<std::remove_cvref_t<T>>::value;
229-
230-
// Range (iterable, not string)
231-
template <typename T, typename = void>
232-
struct is_range : std::false_type
233-
{
234-
};
235-
template <typename T>
236-
struct is_range<T, std::void_t<
237-
decltype(std::begin(std::declval<const T &>())),
238-
decltype(std::end(std::declval<const T &>()))>>
239-
: std::bool_constant<!is_string_like_v<T>>
240-
{
241-
};
242-
template <typename T>
243-
inline constexpr bool is_range_v = is_range<T>::value;
244-
245-
// Map-like (key_type + mapped_type)
246-
template <typename T, typename = void>
247-
struct is_map_like : std::false_type
248-
{
249-
};
250-
template <typename T>
251-
struct is_map_like<T, std::void_t<typename T::key_type, typename T::mapped_type>>
252-
: std::true_type
253-
{
254-
};
255-
template <typename T>
256-
inline constexpr bool is_map_like_v = is_map_like<T>::value;
257-
258-
// Tuple-like
259-
template <typename T, typename = void>
260-
struct is_tuple_like : std::false_type
261-
{
262-
};
263-
template <typename T>
264-
struct is_tuple_like<T, std::void_t<decltype(std::tuple_size<T>::value)>>
265-
: std::true_type
266-
{
267-
};
268-
template <>
269-
struct is_tuple_like<std::string> : std::false_type
270-
{
271-
};
272-
template <typename T>
273-
inline constexpr bool is_tuple_like_v = is_tuple_like<std::remove_cvref_t<T>>::value;
274-
275-
// Pair
276-
template <typename T>
277-
struct is_pair : std::false_type
278-
{
279-
};
280-
template <typename A, typename B>
281-
struct is_pair<std::pair<A, B>> : std::true_type
282-
{
283-
};
284-
template <typename T>
285-
inline constexpr bool is_pair_v = is_pair<std::remove_cvref_t<T>>::value;
286-
287-
// Smart pointers
288-
template <typename T>
289-
struct is_unique_ptr : std::false_type
290-
{
291-
};
292-
template <typename T, typename D>
293-
struct is_unique_ptr<std::unique_ptr<T, D>> : std::true_type
294-
{
295-
};
296-
template <typename T>
297-
struct is_shared_ptr : std::false_type
298-
{
299-
};
300-
template <typename T>
301-
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type
302-
{
303-
};
304-
template <typename T>
305-
struct is_weak_ptr : std::false_type
306-
{
307-
};
308-
template <typename T>
309-
struct is_weak_ptr<std::weak_ptr<T>> : std::true_type
310-
{
311-
};
312-
313-
// Optional / variant / any
314-
template <typename T>
315-
struct is_optional : std::false_type
316-
{
317-
};
318-
template <typename T>
319-
struct is_optional<std::optional<T>> : std::true_type
320-
{
321-
};
322-
template <typename T>
323-
inline constexpr bool is_optional_v = is_optional<std::remove_cvref_t<T>>::value;
324-
325-
template <typename T>
326-
struct is_variant : std::false_type
327-
{
328-
};
329-
template <typename... Ts>
330-
struct is_variant<std::variant<Ts...>> : std::true_type
331-
{
332-
};
333-
template <typename T>
334-
inline constexpr bool is_variant_v = is_variant<std::remove_cvref_t<T>>::value;
335-
336-
template <typename T>
337-
inline constexpr bool is_any_v = std::is_same_v<std::remove_cvref_t<T>, std::any>;
338-
339-
// Chrono
340-
template <typename T>
341-
struct is_duration : std::false_type
342-
{
343-
};
344-
template <typename Rep, typename Period>
345-
struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type
346-
{
347-
};
348-
template <typename T>
349-
inline constexpr bool is_duration_v = is_duration<std::remove_cvref_t<T>>::value;
350-
351-
template <typename T>
352-
struct is_time_point : std::false_type
353-
{
354-
};
355-
template <typename C, typename D>
356-
struct is_time_point<std::chrono::time_point<C, D>> : std::true_type
357-
{
358-
};
359-
template <typename T>
360-
inline constexpr bool is_time_point_v = is_time_point<std::remove_cvref_t<T>>::value;
361-
362-
// Misc
363-
template <typename T>
364-
inline constexpr bool is_fs_path_v =
365-
std::is_same_v<std::remove_cvref_t<T>, std::filesystem::path>;
366-
367-
template <typename T>
368-
struct is_ref_wrapper : std::false_type
369-
{
370-
};
371-
template <typename T>
372-
struct is_ref_wrapper<std::reference_wrapper<T>> : std::true_type
373-
{
374-
};
375-
template <typename T>
376-
inline constexpr bool is_ref_wrapper_v = is_ref_wrapper<std::remove_cvref_t<T>>::value;
377-
378-
template <typename T>
379-
inline constexpr bool is_nullptr_v =
380-
std::is_same_v<std::remove_cvref_t<T>, std::nullptr_t>;
381-
382-
template <typename T>
383-
inline constexpr bool is_bool_v =
384-
std::is_same_v<std::remove_cvref_t<T>, bool>;
385-
386-
template <typename T>
387-
inline constexpr bool is_char_v =
388-
std::is_same_v<std::remove_cvref_t<T>, char> ||
389-
std::is_same_v<std::remove_cvref_t<T>, signed char> ||
390-
std::is_same_v<std::remove_cvref_t<T>, unsigned char> ||
391-
std::is_same_v<std::remove_cvref_t<T>, wchar_t> ||
392-
std::is_same_v<std::remove_cvref_t<T>, char8_t> ||
393-
std::is_same_v<std::remove_cvref_t<T>, char16_t> ||
394-
std::is_same_v<std::remove_cvref_t<T>, char32_t>;
395-
396-
// Container adapters
397-
template <typename T>
398-
struct is_stack : std::false_type
399-
{
400-
};
401-
template <typename T, typename C>
402-
struct is_stack<std::stack<T, C>> : std::true_type
403-
{
404-
};
405-
template <typename T>
406-
struct is_queue : std::false_type
407-
{
408-
};
409-
template <typename T, typename C>
410-
struct is_queue<std::queue<T, C>> : std::true_type
411-
{
412-
};
413-
template <typename T>
414-
struct is_priority_queue : std::false_type
415-
{
416-
};
417-
template <typename T, typename C, typename Cmp>
418-
struct is_priority_queue<std::priority_queue<T, C, Cmp>> : std::true_type
419-
{
420-
};
421-
422-
// ADL vix_inspect detection
423-
template <typename T, typename = void>
424-
struct has_vix_inspect_hook : std::false_type
425-
{
426-
};
427-
template <typename T>
428-
struct has_vix_inspect_hook<T, std::void_t<
429-
decltype(vix_inspect(std::declval<inspect_context &>(), std::declval<const T &>()))>>
430-
: std::true_type
431-
{
432-
};
433-
template <typename T>
434-
inline constexpr bool has_vix_inspect_hook_v =
435-
has_vix_inspect_hook<std::remove_cvref_t<T>>::value;
436-
437-
// inspector<T> specialization detection
438-
template <typename T, typename = void>
439-
struct has_inspector_specialization : std::false_type
440-
{
441-
};
442-
template <typename T>
443-
struct has_inspector_specialization<T, std::void_t<
444-
decltype(inspector<T>::inspect(
445-
std::declval<inspect_context &>(),
446-
std::declval<const T &>()))>>
447-
: std::true_type
448-
{
449-
};
450-
template <typename T>
451-
inline constexpr bool has_inspector_v =
452-
has_inspector_specialization<std::remove_cvref_t<T>>::value;
453-
454-
// field_map detection
455-
template <typename T, typename = void>
456-
struct has_field_map : std::false_type
457-
{
458-
};
459-
template <typename T>
460-
struct has_field_map<T, std::void_t<decltype(field_map<T>::fields())>>
461-
: std::true_type
462-
{
463-
};
464-
template <typename T>
465-
inline constexpr bool has_field_map_v =
466-
has_field_map<std::remove_cvref_t<T>>::value;
467-
468-
#if VIX_HAS_EXPECTED
469-
template <typename T>
470-
struct is_expected : std::false_type
471-
{
472-
};
473-
template <typename T, typename E>
474-
struct is_expected<std::expected<T, E>> : std::true_type
475-
{
476-
};
477-
template <typename T>
478-
inline constexpr bool is_expected_v = is_expected<std::remove_cvref_t<T>>::value;
479-
#endif
480-
481-
} // namespace traits
482-
483-
namespace concepts
484-
{
485-
486-
template <typename T>
487-
concept Ostreamable = traits::is_ostreamable_v<T>;
488-
template <typename T>
489-
concept StringLike = traits::is_string_like_v<T>;
490-
template <typename T>
491-
concept Range = traits::is_range_v<T>;
492-
template <typename T>
493-
concept MapLike = traits::is_map_like_v<T>;
494-
template <typename T>
495-
concept TupleLike = traits::is_tuple_like_v<T> && !Range<T>;
496-
template <typename T>
497-
concept HasVixInspect = traits::has_vix_inspect_hook_v<T>;
498-
template <typename T>
499-
concept HasInspector = traits::has_inspector_v<T>;
500-
template <typename T>
501-
concept HasFieldMap = traits::has_field_map_v<T>;
502-
template <typename T>
503-
concept Duration = traits::is_duration_v<T>;
504-
template <typename T>
505-
concept TimePoint = traits::is_time_point_v<T>;
506-
507-
} // namespace concepts
508-
509170
/**
510171
* @brief Complete set of compile-time and runtime metadata for a type T.
511172
*
@@ -2083,10 +1744,10 @@ namespace vix
20831744
* @brief Inspect a value mid-expression and return it unchanged.
20841745
*
20851746
* @example
2086-
* auto result = vix::tap(compute()); // prints and returns
1747+
* auto result = vix::inspect_tap(compute()); // prints and returns
20871748
*/
20881749
template <typename T>
2089-
const T &tap(const T &value, std::string_view label = "")
1750+
const T &inspect_tap(const T &value, std::string_view label = "")
20901751
{
20911752
auto &os = *default_options().out;
20921753
if (!label.empty())

0 commit comments

Comments
 (0)