summaryrefslogtreecommitdiff
path: root/tiger-compiler/lib/misc/lambda-visitor.hh
blob: db0b40356cbcb683beebad6728226a9a81820325 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
 ** \file misc/lambda-visitor.hh
 ** \brief Declaration of misc::LambdaVisitor.
 **/

#pragma once

namespace misc
{
  /** \brief Allows to build a visitor from lambdas to use with std::visit.
   **
   ** This allows to use a syntax such as
   ** std::visit(
   **   LambdaVisitor{
   **     Lambda1,
   **     Lambda2,
   **     ...
   **   }, variant);
   ** to a call specific lambdas depending on the static type of the variant.
   **
   ** For instance, the following code:
   **
   ** std::visit(
   **   LambdaVisitor{
   **     [](int) { std::cout << "It's an int!\n"; },
   **     [](std::string) { std::cout << "It's a string!\n"; },
   **     [](auto) { std::cout << "It's anything else!\n"; },
   **   }, var);
   **
   ** would have the following results for these variants:
   ** std::variant<int, std::string, char> v1 = 1; // It's an int!
   ** std::variant<int, std::string, char> v2 = "Tigrou"; // It's a string!
   ** std::variant<int, std::string, char> v3 = 'a'; // It's anything else!
   **
   ** This allows to perform quick and efficient pattern matching on variants'
   ** static type.
   **
   ** The really powerful thing here is that this can be used to match multiple
   ** variants. For instance the following code:
   **
   ** std::visit(
   **   LambdaVisitor{
   **     [](int, std::string) { ... },
   **     [](auto, std::string) { ... },
   **     [](auto, auto) { ... },
   **   }, var1, var2);
   **
   ** would call the first lambda if var1 is an int and var2 a string, the
   ** second lambda if var2 is a string and var1 anything else than an int,
   ** and the third in any other case.
   **
   ** Do note that you can have a return value for your lambdas, and have
   ** something like so:
   ** int a = std::visit(
   **   LambdaVisitor{
   **     ...
   **   }, var);
   **
   **
   ** Be careful , you must necessarily have a matching lambda for any possible
   ** values of the variants used as arguments! You won't be able to compile
   ** otherwise.
   **/
  template <class... Ts> struct LambdaVisitor : Ts...
  {
    /** C++ actually desugars lambdas to some kind of struct functors.
     ** For instance, the following lambda could be desugared to the following
     ** struct:
     **
     ** auto f = [](int a) { std::cout << a; }
     **
     ** struct {
     **   auto operator()(int a) {
     **     std::cout << a;
     **   }
     ** } f;
     **
     ** We therefore want to access the operator() of every lambda/functor used
     ** to create the LambdaVisitor.
     **/
    using Ts::operator()...;
  };

  /// Class template argument deduction. This allows to implicitly deduce the
  /// templated type of the visitor created from the types of the arguments
  /// it is constructed with, without having to explicitly fill the template.
  template <class... Ts> LambdaVisitor(Ts...) -> LambdaVisitor<Ts...>;

} // namespace misc