plot
Header-only C++ SVG charts — heatmap, line, scatter — GR-style API + Solarized themes
Loading...
Searching...
No Matches
meta.hpp
1/* Copyright (c) 2026 Steven Varga, Toronto, ON, Canada
2 * MIT License — see LICENSE
3 *
4 * Compile-time argument dispatch for the dependency-free SVG plotting layer:
5 * order-independent named arguments (`plot::arg::tpos`/`get`/`getn`/`required`),
6 * compile-time tuple iteration (`plot::impl::static_for`), and the detection
7 * idiom (`plot::impl::is_detected`). This is a self-contained copy — the plot
8 * layer carries its own machinery so it compiles with ZERO sibling libraries on
9 * the include path. Pure C++ — no third-party or platform dependency.
10 */
11#ifndef PLOT_META_HPP
12#define PLOT_META_HPP
13
14#include <type_traits>
15#include <optional>
16#include <cstddef>
17#include <utility>
18#include <tuple>
19#include <functional>
20
21namespace plot::impl::compat {
22// N4436 and https://en.cppreference.com/w/cpp/experimental/is_detected
23
24 struct nonesuch {
25 nonesuch( ) = delete;
26 nonesuch( nonesuch const& ) = delete;
27 void operator = ( nonesuch const& ) = delete;
28 };
29
30 template< class... > using void_t = void;
31 namespace detail {
32 template <class Default, class AlwaysVoid,
33 template<class...> class Op, class... Args>
34 struct detector {
35 using value_t = std::false_type;
36 using type = Default;
37 };
38
39 template <class Default, template<class...> class Op, class... Args>
40 struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
41 using value_t = std::true_type;
42 using type = Op<Args...>;
43 };
44 } // namespace detail
45
46 template <template<class...> class Op, class... Args>
47 using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
48
49 template <template<class...> class Op, class... Args>
50 using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
51
52 template <class Default, template<class...> class Op, class... Args>
53 using detected_or = detail::detector<Default, void, Op, Args...>;
54
55 // helper templates
56 template< template<class...> class Op, class... Args >
57 constexpr bool is_detected_v = is_detected<Op, Args...>::value;
58 template< class Default, template<class...> class Op, class... Args >
59 using detected_or_t = typename detected_or<Default, Op, Args...>::type;
60 template <class Expected, template<class...> class Op, class... Args>
61 using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
62 template <class Expected, template<class...> class Op, class... Args>
63 constexpr bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value;
64 template <class To, template<class...> class Op, class... Args>
65 using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;
66 template <class To, template<class...> class Op, class... Args>
67 constexpr bool is_detected_convertible_v = is_detected_convertible<To, Op, Args...>::value;
68}
69
70namespace plot::impl::detail {
71 template <std::size_t S, class callable_t, std::size_t ...Is>
72 constexpr void static_for( callable_t&& callback, std::integer_sequence<std::size_t, Is...> ){
73 ( callback( std::integral_constant<std::size_t, S + Is>{ } ),... );
74 }
75}
76namespace plot::impl {
77 // tuple_t -- tuple we are to iterate through
78 // S -- start value
79 // callable_t -- Callable object
80 template <class tuple_t, std::size_t S = 0, class callable_t >
81 constexpr void static_for( callable_t&& callback ){
82 constexpr std::size_t N = std::tuple_size<tuple_t>::value;
83 detail::static_for<S, callable_t>(
84 std::forward<callable_t>(callback),
85 std::make_integer_sequence<std::size_t, N - S>{ } );
86 }
87}
88
89namespace plot::impl { // feature detection
90 using plot::impl::compat::is_detected;
91 template<class T> using value_t = typename T::value_type;
92 template<class T> using get_value_type = compat::detected_or<compat::nonesuch, value_t, T>;
93 template<class T> using has_value_type = is_detected<value_t, T>;
94 // default case
95 template <class T, class TagOrClass, class E = void> struct is_value_type {
96 constexpr static bool value = false;
97 };
98 // templated classes are tagged for identification
99 template <class T, class TagOrClass> struct is_value_type <T, TagOrClass,
100 typename std::enable_if<has_value_type<T>::value>::type > {
101 constexpr static bool value = std::is_convertible<typename impl::get_value_type<T>::type, TagOrClass>::value;
102 };
103 // enum classes can't be tagged, nor pods may need tagging
104 template <class T, class TagOrClass> struct is_value_type <T, TagOrClass,
105 typename std::enable_if<!has_value_type<T>::value>::type > {
106 constexpr static bool value = std::is_convertible<T, TagOrClass>::value;
107 };
108}
109
110namespace plot::arg {
111 template <class T>
112 struct optional : public std::optional<T> {
113 using std::optional<T>::optional;
114 };
115}
116
117namespace plot::arg {
118 // declaration
119 template<class S, class... T > struct tpos;
120 namespace detail {
121 // declaration
122 template<class search_pattern, int position, int count, bool branch, class prev_head, class arguments> struct tuple_pos;
123 // initialization case
124 template<class S, int P, int C, bool B, class not_used, class... Tail >
125 struct tuple_pos<S, P,C, B, not_used, std::tuple<Tail...>>
126 : tuple_pos<S, P,C, false, not_used, std::tuple<Tail...>> { };
127 // recursive case
128 template<class S, int P, int C, class not_used, class Head, class... Tail >
129 struct tuple_pos<S, P,C,false, not_used, std::tuple<Head, Tail...>>
130 : tuple_pos<S, P+1,C, impl::is_value_type<Head,S>::value,
131 Head, std::tuple<Tail...>> { };
132 // match case
133 template<class S, int P, int C, class Type, class... Tail >
134 struct tuple_pos<S, P,C, true, Type, std::tuple<Tail...>>
135 : std::integral_constant<int,P>{
136 static constexpr int position = P;
137 using type = Type;
138 static constexpr bool present = true;
139 };
140 // default case
141 template<class S, class H, int P, int C>
142 struct tuple_pos<S, P,C, false, H, std::tuple<>>
143 : std::integral_constant<int,-1> {
144 using type = bool;
145 static constexpr bool present = false;
146 };
147 }
148 template<class S, class... args_t > struct tpos
149 : detail::tuple_pos<const S&, -1, 0, false, void, std::tuple<args_t...>>{ };
150
151 /* un-tagged case */
152 template <class T, class... args_t,
153 class idx_t = tpos<typename T::value_type, args_t...>>
154 typename std::enable_if<impl::has_value_type<T>::value, arg::optional<T>
155 >::type get( args_t... args ){
156 auto tuple = std::forward_as_tuple( args... );
157 if constexpr ( idx_t::present )
158 return std::get<idx_t::value>( tuple );
159 else
160 return std::nullopt;
161 }
162
163 template <class T, class... args_t, class idx_t = tpos<T, args_t...>>
164 constexpr typename std::enable_if<!impl::has_value_type<T>::value,
165 typename idx_t::type>::type get( args_t... args ){
166 auto tuple = std::forward_as_tuple( args... );
167 if constexpr ( idx_t::present )
168 return std::get<idx_t::value>( tuple );
169 else
170 return false;
171 }
172
173 template <class T, class... args_t, class idx_t = tpos<T, args_t...>>
174 typename std::enable_if<!impl::has_value_type<T>::value,
175 typename idx_t::type>::type required( const char msg[3], args_t... args ) {
176 auto tuple = std::forward_as_tuple( args... );
177 if constexpr ( idx_t::present )
178 return std::get<idx_t::value>( tuple );
179 else {
180 static_assert( idx_t::present );
181 return false;
182 }
183 }
184
185 template <int idx, class... args_t>
186 typename std::tuple_element<idx, std::tuple<args_t...> >::type&
187 getn(args_t&&... args ){
188 auto tuple = std::forward_as_tuple(args...);
189 return std::get<idx>( tuple );
190 }
191
192
193 template<typename T> struct fn_t;
194 template<typename R, typename ...Args>
195 struct fn_t<std::function<R(Args...)>> {
196 static const std::size_t nargs = sizeof...(Args);
197 typedef R result_type;
198
199 template <std::size_t I>
200 struct arg {
201 typedef typename std::tuple_element<I, std::tuple<Args...>>::type type;
202 };
203 };
204}
205#endif