Quetzal-CoaTL
The Coalescence Template Library
Loading...
Searching...
No Matches
expressive.hpp
1// Expressive library
2// Copyright Ambre Marques 2016 - 2017.
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7#ifndef MAQUETTE_EXPRESSIVE_H
8#define MAQUETTE_EXPRESSIVE_H
9
10#include <functional>
11#include <utility>
12
13#include <iostream>
14#include <string>
15#include <type_traits>
16
17#include <tuple>
18#include <type_traits>
19
20namespace std
21{
22
23// common_type<tuple<T...>, tuple<U...>> -> tuple<common_type<T, U>...>
24template <typename... T, typename... U> struct common_type<std::tuple<T...>, std::tuple<U...>>
25{
26 using type = std::tuple<typename std::common_type<T, U>::type...>;
27};
28
29} // namespace std
30
31namespace meta
32{
33
34template <bool b, bool... bools> inline constexpr bool all()
35{
36 return b && all<bools...>();
37}
38
39template <> constexpr bool all<true>()
40{
41 return true;
42}
43template <> constexpr bool all<false>()
44{
45 return false;
46}
47
48} // namespace meta
49
50namespace quetzal
51{
52namespace expressive
53{
54
55// general case: Callable being a class, use operator() properties
56template <typename Callable> struct Callable_traits : public Callable_traits<decltype(&Callable::operator())>
57{
58};
59
60// free function pointer
61template <typename Ret, typename... Args> struct Callable_traits<Ret (*)(Args...)>
62{
63 using return_type = Ret;
64 using arguments_type = std::tuple<Args...>;
65 static constexpr std::size_t arguments_count = sizeof...(Args);
66};
67
68// function type
69template <typename Ret, typename... Args> struct Callable_traits<Ret(Args...)>
70{
71 using return_type = Ret;
72 using arguments_type = std::tuple<Args...>;
73 static constexpr std::size_t arguments_count = sizeof...(Args);
74};
75
76// member function type
77template <typename Ret, typename Class, typename... Args> struct Callable_traits<Ret (Class::*)(Args...) const>
78{
79 using return_type = Ret;
80 using arguments_type = std::tuple<Args...>;
81 static constexpr std::size_t arguments_count = sizeof...(Args);
82};
83
84template <typename Callable> using return_type = typename Callable_traits<Callable>::return_type;
85
86template <typename Callable> using arguments_type = typename Callable_traits<Callable>::arguments_type;
87
88template <typename Callable, std::size_t I>
89using argument_type = typename std::tuple_element<I, typename Callable_traits<Callable>::arguments_type>::type;
90
91template <typename Callable> constexpr auto arguments_count()
92{
94}
95
96template <typename Functor, typename Interface>
97using functor_has_interface = std::is_convertible<Interface, typename Callable_traits<Functor>::arguments_type>;
98
99template <typename Functor, typename... Args>
100using callable_with = std::is_convertible<arguments_type<void(Args...)>, arguments_type<Functor>>;
101
102/*
103support base class for functors delagating their operator() to other functor(s).
104*/
105template <typename... Functors> struct composite_functor
106{
107 using functors_type = std::tuple<Functors...>;
108
109 template <std::size_t I> using return_type = return_type<typename std::tuple_element<I, functors_type>::type>;
110
111 template <std::size_t I> static constexpr std::size_t arguments_count()
112 {
114 }
115
116 using arguments_type = typename std::common_type<typename Callable_traits<Functors>::arguments_type...>::type;
117
118 constexpr composite_functor(Functors const &...functors) : functors(functors...)
119 {
120 // all Functors shall have the same call arguments (which will be those of the resulting functor)
121 static_assert(meta::all<functor_has_interface<Functors, arguments_type>::value...>(),
122 "incompatible functors (arguments differs)");
123 }
124
125 // callability check
126 template <typename... Args> static constexpr bool call_check()
127 {
128 // shall we check against interface or against each functors
129 return meta::all<callable_with<Functors, Args...>::value...>();
130 }
131
132 template <std::size_t I> constexpr auto const &f() const
133 {
134 return std::get<I>(functors);
135 }
136
137 functors_type functors;
138};
139
140// unary compositing specialization
141template <typename F> struct composite_functor<F>
142{
143 using return_type = typename Callable_traits<F>::return_type;
144 using arguments_type = typename Callable_traits<F>::arguments_type;
145
146 static constexpr std::size_t arguments_count()
147 {
149 }
150
151 constexpr composite_functor(F const &f) : f(f)
152 {
153 }
154
155 // callability check
156 template <typename... Args> static constexpr bool call_check()
157 {
158 return callable_with<F, Args...>::value;
159 }
160
161 F f;
162};
163
164// ****** transforming functions into operator friendly classes ****** //
165
166template <typename F> struct make_expression_t;
167
168template <typename F> struct Callable_traits<make_expression_t<F>> : public Callable_traits<F>
169{
170};
171
172template <typename F> struct make_expression_t : public composite_functor<F>
173{
175 using value_type = typename base_t::return_type;
176
177 constexpr make_expression_t(F f) : base_t(f)
178 {
179 }
180
181 template <typename... Args>
182 constexpr typename std::enable_if<base_t::template call_check<Args...>(), value_type>::type operator()(
183 Args... args) const
184 {
185 return base_t::f(args...);
186 }
187};
188
189template <typename F> constexpr auto expression(F f)
190{
191 return make_expression_t<F>(f);
192}
193
205template <typename F> constexpr auto use(F f)
206{
207 return make_expression_t<F>(f);
208}
209
210template <typename F> constexpr auto function(F f)
211{
212 return make_expression_t<F>(f);
213}
214
215// ****** literal generation ****** //
216
217template <typename T, typename... Args> struct literal_t
218{
219 const T value;
220 constexpr literal_t(T const &value) : value(value)
221 {
222 }
223 constexpr T operator()(Args...) const
224 {
225 return value;
226 }
227};
228
243template <typename... Args, typename T> constexpr auto literal(T t)
244{
245 return literal_t<T, Args...>{t};
246}
247
262template <typename... Args> struct literal_factory
263{
264 template <typename T> constexpr auto operator()(T t)
265 {
266 return literal<Args...>(t);
267 }
268};
269
270// ****** operator overloading ****** //
271
272template <typename Operator, typename T>
273using applied_unary_type = decltype(std::declval<Operator>()(std::declval<T>()));
274
275template <typename Op, typename T>
276constexpr auto is_applicable_unary_impl(int) -> decltype(std::declval<applied_unary_type<Op, T>>(), bool())
277{
278 return true;
279}
280
281template <typename Op, typename T> constexpr bool is_applicable_unary_impl(...)
282{
283 return false;
284}
285
286template <typename A, typename Operator, typename B>
287using applied_binary_type = decltype(std::declval<Operator>()(std::declval<A>(), std::declval<B>()));
288
289template <typename A, typename Op, typename B>
290constexpr auto is_applicable_binary_impl(int) -> decltype(std::declval<applied_binary_type<A, Op, B>>(), bool())
291{
292 return true;
293}
294
295template <typename A, typename Op, typename B> constexpr bool is_applicable_binary_impl(...)
296{
297 return false;
298}
299
300// Operator is expected to be std::plus<void> or so
301
302template <typename Operator, typename T> constexpr bool is_applicable_unary()
303{
304 return is_applicable_unary_impl<Operator, T>(0);
305}
306
307template <typename A, typename Operator, typename B> constexpr bool is_applicable_binary()
308{
309 return is_applicable_binary_impl<A, Operator, B>(0);
310}
311
312// Operator is expected to be something like std::plus<void>, which has a template operator()
313
314template <typename Operator, typename F> struct unop_t;
315
316template <typename Operator, typename F> struct Callable_traits<unop_t<Operator, F>>
317{
318 using return_type = typename unop_t<Operator, F>::value_type;
319 using arguments_type = typename unop_t<Operator, F>::arguments_type;
320 static constexpr std::size_t arguments_count = Callable_traits<F>::arguments_count;
321};
322
323template <typename Operator, typename F> struct unop_t : public composite_functor<F>
324{
326 using value_type = applied_unary_type<Operator, typename base_t::return_type>;
327
328 unop_t(F f) : base_t(f)
329 {
330 static_assert(is_applicable_unary<Operator, typename base_t::return_type>(), "incompatible functor value");
331 }
332
333 template <typename... Args> constexpr value_type operator()(Args... args) const
334 {
335 static_assert(base_t::template call_check<Args...>(), "wrong arguments types or count");
336 // use a sub function to shut up compiler, we already have an assert
337 return impl(args...);
338 }
339
340 template <typename... Args> constexpr value_type impl(Args... args) const
341 {
342 return Operator{}(base_t::f(args...));
343 }
344};
345
346/* symetric binop over functors */
347
348template <typename FL, typename Operator, typename FR> struct symetric_binop_t;
349
350template <typename FL, typename Operator, typename FR> struct Callable_traits<symetric_binop_t<FL, Operator, FR>>
351{
352 using return_type = typename symetric_binop_t<FL, Operator, FR>::value_type;
353 using arguments_type = typename symetric_binop_t<FL, Operator, FR>::arguments_type;
354 static constexpr std::size_t arguments_count = std::tuple_size<arguments_type>::arguments_count;
355};
356
357template <typename FL, typename Operator, typename FR>
358struct symetric_binop_t : composite_functor<FL, FR>, private Operator
359{
361
362 using FL_return_type = typename base_t::template return_type<0>;
363 using FR_return_type = typename base_t::template return_type<1>;
364
365 using value_type = applied_binary_type<FL_return_type, Operator, FR_return_type>;
366
367 constexpr symetric_binop_t(FL l, FR r) : base_t(l, r)
368 {
369 static_assert(is_applicable_binary<FL_return_type, Operator, FR_return_type>(),
370 "incompatible functors value types");
371 }
372
373 template <typename... Args> constexpr value_type operator()(Args... args) const
374 {
375 static_assert(base_t::template call_check<Args...>(), "wrong arguments types or count");
376
377 return Operator::operator()(base_t::template f<0>()(args...), base_t::template f<1>()(args...));
378 }
379
380 template <typename... Args> constexpr value_type impl(Args... args) const
381 {
382 return Operator::operator()(base_t::template f<0>()(args...), base_t::template f<1>()(args...));
383 }
384};
385
386/* ****** math around functions ****** */
387/*
388template <typename F>
389constexpr auto operator!(F f) {return unop_t<std::not<void>, F>{f};}
390*/
391template <typename F> constexpr auto operator-(F f)
392{
393 return unop_t<std::negate<void>, F>{f};
394}
395
396template <typename F1, typename F2> constexpr auto operator+(F1 f1, F2 f2)
397{
398 return symetric_binop_t<F1, std::plus<void>, F2>{f1, f2};
399}
400
401template <typename F1, typename F2> constexpr auto operator-(F1 f1, F2 f2)
402{
403 return symetric_binop_t<F1, std::minus<void>, F2>{f1, f2};
404}
405
406template <typename F1, typename F2> constexpr auto operator*(F1 f1, F2 f2)
407{
408 return symetric_binop_t<F1, std::multiplies<void>, F2>{f1, f2};
409}
410
411template <typename F1, typename F2> constexpr auto operator/(F1 f1, F2 f2)
412{
413 return symetric_binop_t<F1, std::divides<void>, F2>{f1, f2};
414}
415
416template <typename F1, typename F2> constexpr auto operator%(F1 f1, F2 f2)
417{
418 return symetric_binop_t<F1, std::modulus<void>, F2>{f1, f2};
419}
420
421/* ****** composing functions ****** */
422
423template <typename Outer, typename... Inners> struct compose_t;
424
425template <typename Outer, typename... Inners> struct Callable_traits<compose_t<Outer, Inners...>>
426{
427 using return_type = typename compose_t<Outer, Inners...>::value_type;
428 using arguments_type = typename compose_t<Outer, Inners...>::arguments_type;
429 static constexpr std::size_t arguments_count = std::tuple_size<arguments_type>::value;
430};
431
432template <typename Outer, typename... Inners> struct compose_t : public composite_functor<Inners...>
433{
434 using base_t = composite_functor<Inners...>;
435 using value_type = return_type<Outer>;
436
437 constexpr compose_t(Outer outer, Inners... inners) : base_t(inners...), outer(outer)
438 {
439 // there must be exactly as many tied functors as Outer arguments
440 static_assert(sizeof...(Inners) == arguments_count<Outer>(), "wrong functors count");
441
442 // each tied functor must return a value compatible with the matching Outer's argument
443 static_assert(composable_check(std::index_sequence_for<Inners...>{}), "uncomposable functors");
444 }
445
446 template <typename... Args> constexpr value_type operator()(Args... args) const
447 {
448 static_assert(base_t::template call_check<Args...>(), "wrong arguments type or count");
449
450 return impl(std::index_sequence_for<Inners...>{}, args...);
451 }
452
453 // *** call details *** //
454 // actual call
455 template <std::size_t... Is, typename... Args>
456 constexpr value_type impl(std::index_sequence<Is...>, Args... args) const
457 {
458 return outer(base_t::template f<Is>()(args...)...);
459 }
460
461 // *** build details *** //
462 template <std::size_t... Is> static constexpr bool composable_check(std::index_sequence<Is...>)
463 {
464 return meta::all<std::is_convertible<return_type<Inners>, argument_type<Outer, Is>>::value...>();
465 }
466
467 Outer outer;
468};
469
470// spécialisation pour le cas où il n'y a qu'une fonction interne (car composite_functor est spécialisé).
471template <typename Outer, typename Inner> struct compose_t<Outer, Inner> : public composite_functor<Inner>
472{
474 using value_type = return_type<Outer>;
475
476 constexpr compose_t(Outer outer, Inner inner) : base_t(inner), outer(outer)
477 {
478 static_assert(arguments_count<Outer>() == 1, "wrong functors count");
479
480 // tied functor must return a value compatible with the argument of Outer
481 static_assert(std::is_convertible<return_type<Inner>, argument_type<Outer, 0>>::value, "uncomposable functors");
482 }
483
484 template <typename... Args> constexpr value_type operator()(Args... args) const
485 {
486 static_assert(base_t::template call_check<Args...>(), "wrong arguments type or count");
487 return impl(args...);
488 }
489
490 template <typename... Args> constexpr value_type impl(Args... args) const
491 {
492 return outer(base_t::f(args...));
493 }
494
495 Outer outer;
496};
497
498// spécialisation pour le cas où Inner est vide.
499
500template <typename Outer> struct compose_t<Outer> : public composite_functor<Outer>
501{
503 using value_type = typename base_t::return_type;
504
505 constexpr compose_t(Outer f) : base_t(f)
506 {
507 }
508
509 template <typename... Args> constexpr value_type operator()(Args... args) const
510 {
511 static_assert(base_t::template call_check<Args...>(), "wrong arguments type or count");
512 return impl(args...);
513 }
514
515 template <typename... Args> constexpr value_type impl(Args... args) const
516 {
517 return f(args...);
518 }
519};
520
530template <typename F, typename... Fs> constexpr auto compose(F f, Fs... fs)
531{
532 return compose_t<F, Fs...>(f, fs...);
533}
534
535/* ****** chaining functions ****** */
536
537template <typename F> constexpr auto chain(F f)
538{
539 return expression(f);
540}
541
551template <typename Inner, typename Outer> constexpr auto operator>>(Inner inner, Outer outer)
552{
553 return compose(outer, inner);
554}
555
565template <typename Inner, typename Outer> constexpr auto operator<<(Outer outer, Inner inner)
566{
567 return compose(outer, inner);
568}
569
580template <typename F1, typename F2, typename... Fs> constexpr auto chain(F1 f1, F2 f2, Fs... fs)
581{
582 return f1 >> chain(f2, fs...);
583}
584
585/* ****** ostreaming functions ****** */
586
587/* suppose que les foncteurs soient affichables, ce qui n'est pas le cas de base. */
588
589template <typename T> std::ostream &operator<<(std::ostream &os, std::negate<T> const &)
590{
591 return os << '-';
592}
593
594template <typename T> std::ostream &operator<<(std::ostream &os, std::plus<T> const &)
595{
596 return os << '+';
597}
598template <typename T> std::ostream &operator<<(std::ostream &os, std::minus<T> const &)
599{
600 return os << '-';
601}
602template <typename T> std::ostream &operator<<(std::ostream &os, std::multiplies<T> const &)
603{
604 return os << '*';
605}
606template <typename T> std::ostream &operator<<(std::ostream &os, std::divides<T> const &)
607{
608 return os << '/';
609}
610template <typename T> std::ostream &operator<<(std::ostream &os, std::modulus<T> const &)
611{
612 return os << '%';
613}
614
615template <typename T, typename... Args> std::ostream &operator<<(std::ostream &os, literal_t<T, Args...> const &l)
616{
617 return os << l.value;
618}
619
620template <typename Operator, typename F> std::ostream &operator<<(std::ostream &os, unop_t<Operator, F> const &f)
621{
622 return os << Operator{} << f.f;
623}
624
625template <typename F1, typename Operator, typename F2>
626std::ostream &operator<<(std::ostream &os, symetric_binop_t<F1, Operator, F2> const &f)
627{
628 return os << f.f1 << Operator{} << f.f2;
629}
630
631} // namespace expressive
632} // namespace quetzal
633
634#endif
constexpr auto compose(F f, Fs... fs)
Mathematical function composition
Definition expressive.hpp:530
constexpr auto literal(T t)
Transforms literals into operator friendly classes.
Definition expressive.hpp:243
constexpr auto operator<<(Outer outer, Inner inner)
Mathematical function composition
Definition expressive.hpp:565
constexpr auto operator>>(Inner inner, Outer outer)
Mathematical function composition
Definition expressive.hpp:551
constexpr auto use(F f)
Transforms functions into operator friendly classes.
Definition expressive.hpp:205
Simulation of coalescence-based models of molecular evolution.
Definition coalescence.hpp:21
Definition expressive.hpp:57
Definition expressive.hpp:433
Definition expressive.hpp:142
Definition expressive.hpp:106
Transforms literals into operator friendly classes.
Definition expressive.hpp:263
Definition expressive.hpp:218
Definition expressive.hpp:173
Definition expressive.hpp:359
Definition expressive.hpp:324