SeqAn3
configuration.hpp
Go to the documentation of this file.
1 // ============================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ============================================================================
4 //
5 // Copyright (c) 2006-2018, Knut Reinert & Freie Universitaet Berlin
6 // Copyright (c) 2016-2018, Knut Reinert & MPI Molekulare Genetik
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 // DAMAGE.
32 //
33 // ============================================================================
34 
40 #pragma once
41 
42 #include <tuple>
43 
44 #include <meta/meta.hpp>
45 
51 #include <seqan3/std/concepts>
52 
53 namespace seqan3::detail
54 {
55 
57 // Forward declaration.
58 template <typename derived_t, typename ... args_t>
59 struct configuration_fn_proxy;
60 
61 template <typename derived_fn_t>
62 class configuration_fn_base;
64 
65 // ----------------------------------------------------------------------------
66 // configuration
67 // ----------------------------------------------------------------------------
68 
128 template <config_element_concept ... configs_t>
129 class configuration : public std::tuple<configs_t...>
130 {
132  template <config_element_concept ... _configs_t>
133  friend class configuration;
134 
135 public:
139  using base_type = std::tuple<configs_t...>;
142 
146  constexpr configuration() = default;
147  constexpr configuration(configuration const &) = default;
148  constexpr configuration(configuration &&) = default;
149  constexpr configuration & operator=(configuration const &) = default;
150  constexpr configuration & operator=(configuration &&) = default;
151  ~configuration() = default;
152 
154  constexpr configuration(std::tuple<configs_t...> const & cfg) : base_type{cfg}
155  {}
156 
158  constexpr configuration(std::tuple<configs_t...> && cfg) : base_type{std::move(cfg)}
159  {}
160 
178  template <typename cfg_fn_t>
180  requires is_type_specialisation_of_v<std::remove_reference_t<cfg_fn_t>, configuration_fn_proxy> ||
181  std::is_base_of_v<configuration_fn_base<remove_cvref_t<cfg_fn_t>>, remove_cvref_t<cfg_fn_t>>
183  constexpr configuration(cfg_fn_t && cfg_fn) :
184  configuration{std::invoke(std::forward<cfg_fn_t>(cfg_fn), configuration<>{})}
185  {}
187 
192  constexpr size_t size() const noexcept
194  {
195  return std::tuple_size_v<base_type>;
196  }
197 
218  template <config_element_concept config_element_t>
219  constexpr auto push_front(config_element_t && cfg_element) const &
220  {
221  return detail::configuration{std::tuple_cat(std::tuple{std::forward<config_element_t>(cfg_element)},
222  static_cast<base_type>(*this))};
223  }
224 
226  template <config_element_concept config_element_t>
227  constexpr auto push_front(config_element_t && cfg_element) &&
228  {
229  return detail::configuration{std::tuple_cat(std::tuple{std::forward<config_element_t>(cfg_element)},
230  std::move(static_cast<base_type>(*this)))};
231  }
232 
250  template <config_element_concept old_config_element_t,
251  config_element_concept new_config_element_t>
252  constexpr auto replace_with(old_config_element_t const & SEQAN3_DOXYGEN_ONLY(old_element),
253  new_config_element_t && new_element) const &
254  {
255  static_assert(std::tuple_size_v<base_type> > 0, "The configuration cannot be empty.");
256  static_assert(meta::find_index<transfer_template_args_onto_t<base_type, type_list>,
257  old_config_element_t>::value != meta::npos::value,
258  "The element to be replaced is not contained in the passed configuration.");
259 
260  auto && [prefix, remainder] = seqan3::tuple_split<old_config_element_t>(static_cast<base_type>(*this));
261 
262  return detail::configuration{std::tuple_cat(std::move(prefix),
263  std::tuple{std::forward<new_config_element_t>(new_element)},
264  tuple_pop_front(std::move(remainder)))};
265  }
266 
268  template <config_element_concept old_config_element_t,
269  config_element_concept new_config_element_t>
270  constexpr auto replace_with(old_config_element_t const & SEQAN3_DOXYGEN_ONLY(old_element),
271  new_config_element_t && new_element) &&
272  {
273  static_assert(std::tuple_size_v<base_type> > 0, "The configuration cannot be empty.");
274  static_assert(meta::find_index<transfer_template_args_onto_t<base_type, type_list>,
275  old_config_element_t>::value != meta::npos::value,
276  "The element to be replaced is not contained in the passed configuration.");
277 
278  auto && [prefix, remainder] =
279  seqan3::tuple_split<old_config_element_t>(std::move(static_cast<base_type>(*this)));
280 
281  return detail::configuration{std::tuple_cat(std::move(prefix),
282  std::tuple{std::forward<new_config_element_t>(new_element)},
283  tuple_pop_front(std::move(remainder)))};
284  }
286 };
287 
293 template <typename cfg_fn_t>
296  requires is_type_specialisation_of_v<std::remove_reference_t<cfg_fn_t>, configuration_fn_proxy> ||
297  std::is_base_of_v<configuration_fn_base<remove_cvref_t<cfg_fn_t>>, remove_cvref_t<cfg_fn_t>>
299 configuration(cfg_fn_t &&) ->
300  configuration<meta::front<detail::tuple_type_list_t<typename std::invoke_result_t<std::remove_reference_t<cfg_fn_t>,
301  configuration<>>::base_type>>>;
303 
304 // ----------------------------------------------------------------------------
305 // Metafunction is_configuration_combinable_with
306 // ----------------------------------------------------------------------------
307 
319 template <typename target_t, typename query_t>
321  requires !is_algorithm_configuration_v<remove_cvref_t<target_t>>
323 struct is_configuration_combinable_with
324 {
326  static constexpr bool value =
327  (std::is_base_of_v<configuration_fn_base<remove_cvref_t<target_t>>, remove_cvref_t<target_t>> ||
328  is_type_specialisation_of_v<remove_cvref_t<target_t>, configuration_fn_proxy>) &&
329  (is_type_specialisation_of_v<remove_cvref_t<query_t>, configuration_fn_proxy> ||
330  (std::is_base_of_v<configuration_fn_base<remove_cvref_t<query_t>>, remove_cvref_t<query_t>> &&
331  !std::is_same_v<remove_cvref_t<target_t>, remove_cvref_t<query_t>>));
332 };
333 
336 template <typename target_t, typename query_t>
338  requires !is_algorithm_configuration_v<remove_cvref_t<target_t>>
340 inline constexpr bool is_configuration_combinable_with_v = is_configuration_combinable_with<target_t, query_t>::value;
341 
342 // ----------------------------------------------------------------------------
343 // configuration_fn_base
344 // ----------------------------------------------------------------------------
345 
356 template <typename derived_fn_t>
357 class configuration_fn_base
358 {
359 protected:
360 
365  configuration_fn_base() = default;
366  configuration_fn_base(configuration_fn_base const &) = default;
367  configuration_fn_base(configuration_fn_base &&) = default;
368  configuration_fn_base & operator=(configuration_fn_base const &) = default;
369  configuration_fn_base & operator=(configuration_fn_base &&) = default;
370  ~configuration_fn_base() = default;
372 
373 public:
374 
385  template <typename configuration_t,
386  typename ... args_t>
387  constexpr auto operator()(configuration_t && configuration,
388  args_t && ... args) const
390  requires is_algorithm_configuration_v<remove_cvref_t<configuration_t>>
392  {
393  return static_cast<derived_fn_t const &>(*this).invoke(std::forward<configuration_t>(configuration),
394  std::forward<args_t>(args)...);
395  }
396 
404  template <typename ... args_t>
405  constexpr configuration_fn_proxy<derived_fn_t, args_t...> operator()(args_t && ... args) const
406  {
407  return {std::forward<args_t>(args)...};
408  }
409 };
410 
425 template <typename configuration_t,
426  typename fn_t>
427 constexpr auto operator|(configuration_t && cfg,
428  fn_t && fn)
430  requires is_algorithm_configuration_v<remove_cvref_t<configuration_t>> &&
431  (is_type_specialisation_of_v<remove_cvref_t<fn_t>, configuration_fn_proxy> ||
432  std::is_base_of_v<configuration_fn_base<remove_cvref_t<fn_t>>, remove_cvref_t<fn_t>>)
434 {
435  return fn(std::forward<configuration_t>(cfg));
436 }
437 
457 template <typename lhs_fn_t,
458  typename rhs_fn_t>
459 constexpr auto operator|(lhs_fn_t && lhs_fn,
460  rhs_fn_t && rhs_fn)
462  requires is_configuration_combinable_with_v<lhs_fn_t, rhs_fn_t>
464 {
465  return rhs_fn(std::forward<lhs_fn_t>(lhs_fn)(configuration<>{}));
466 }
468 
469 // ----------------------------------------------------------------------------
470 // configuration_fn_proxy
471 // ----------------------------------------------------------------------------
472 
486 template <typename derived_t, typename ... args_t>
487 struct configuration_fn_proxy
488 {
489 protected:
490 
492  template <typename t>
493  friend class configuration_fn_base;
494 
496  constexpr configuration_fn_proxy(args_t && ... args) : args_cache{std::forward<args_t>(args)...}
497  {}
498 
503  template <typename configuration_t, std::size_t ... Is>
504  constexpr auto explode(configuration_t && cfg, std::index_sequence<Is...> /*unused*/) &&
505  {
506  // Move out the elements from cache.
507  return derived_t{}(std::forward<configuration_t>(cfg), std::move(std::get<Is>(args_cache))...);
508  }
509 
510  template <typename configuration_t, std::size_t ... Is>
511  constexpr auto explode(configuration_t && cfg, std::index_sequence<Is...> /*unused*/) const &
512  {
513  // Copy the elements from cache.
514  return derived_t{}(std::forward<configuration_t>(cfg), std::get<Is>(args_cache)...);
515  }
517 
519  std::tuple<args_t...> args_cache;
520 
521 public:
525  template <typename configuration_t>
526  constexpr auto operator()(configuration_t && cfg) &&
527  {
528  return explode(std::forward<configuration_t>(cfg), std::make_index_sequence<sizeof...(args_t)>{});
529  }
530 
531  template <typename configuration_t>
532  constexpr auto operator()(configuration_t && cfg) const &
533  {
534  return explode(std::forward<configuration_t>(cfg), std::make_index_sequence<sizeof...(args_t)>{});
535  }
537 };
538 
539 // ============================================================================
540 // Utility functions
541 // ============================================================================
542 
543 // ----------------------------------------------------------------------------
544 // apply_deferred_configs
545 // ----------------------------------------------------------------------------
546 
548 // TODO Document me!
549 template <std::size_t N, typename fn_t, typename config_t>
550 constexpr auto apply_deferred_configs(fn_t && fn,
551  config_t && config)
552  requires is_algorithm_configuration_v<remove_cvref_t<config_t>>
553 {
554  if constexpr (N == 0)
555  {
556  return fn(std::forward<config_t>(config));
557  }
558  else
559  {
560  using config_as_list = detail::transfer_template_args_onto_t<remove_cvref_t<config_t>, type_list>;
561  using current_config_t = meta::at_c<config_as_list, meta::size<config_as_list>::value - N>;
562 
563  auto delegate = [&fn](auto && new_config)
564  {
565  return apply_deferred_configs<N-1>(fn, std::forward<decltype(new_config)>(new_config));
566  };
567 
568  if constexpr (std::is_invocable_v<current_config_t, decltype(delegate), remove_cvref_t<config_t>>)
569  {
570  return current_config_t{}(delegate, std::forward<config_t>(config));
571  }
572  else
573  { // Perfect forwarding of the underlying config.
574  return apply_deferred_configs<N - 1>(fn, std::forward<config_t>(config));
575  }
576  }
577 }
578 
579 template <typename fn_t, typename config_t>
580 constexpr auto apply_deferred_configs(fn_t & fn,
581  config_t && config)
582  requires is_algorithm_configuration_v<remove_cvref_t<config_t>> &&
583  std::Invocable<std::remove_reference_t<fn_t>, std::remove_reference_t<config_t>>
584 {
585  using type_list_t = detail::tuple_type_list_t<typename std::remove_reference_t<config_t>::base_type>;
586  return apply_deferred_configs<meta::size<type_list_t>::value>(std::forward<fn_t>(fn),
587  std::forward<config_t>(config));
588 }
590 } // namespace seqan3::detail
591 
592 namespace std
593 {
595 
599 template <seqan3::detail::config_element_concept ... configs_t>
600 struct tuple_size<seqan3::detail::configuration<configs_t...>>
601 {
603  static constexpr size_t value = std::tuple_size_v<typename seqan3::detail::configuration<configs_t...>::base_type>;
604 };
605 
609 template <size_t pos, seqan3::detail::config_element_concept ... configs_t>
610 struct tuple_element<pos, seqan3::detail::configuration<configs_t...>>
611 {
613  using type = std::tuple_element_t<pos, typename seqan3::detail::configuration<configs_t...>::base_type>;
614 };
616 } //namespace std
Provides concepts for the configuration classes.
Provides seqan3::type_list and auxiliary metafunctions.
Specifies whether the given callable is invocable with the given arguments.
SeqAn specific customisations in the standard namespace.
Definition: align_result.hpp:221
::ranges::size size
Alias for ranges::size. Obtains the size of a range whose size can be calculated in constant time...
Definition: ranges:195
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:58
Provides seqan3::type_list and auxiliary metafunctions.
The Concepts library.
Provides utility functions for tuple like interfaces.
Definition: aligned_sequence_concept.hpp:288
Provides various metafunctions on generic types.
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators. !
Definition: validators.hpp:671
constexpr auto tuple_pop_front(tuple_t &&t)
Removes the first element of a tuple.
Definition: tuple_utility.hpp:194
meta::list< types... > type_list
Type that contains multiple types, an alias for meta::list.
Definition: type_list.hpp:54