Skip to content

Commit a37d707

Browse files
committedJan 11, 2022
First doc pass done
1 parent 99620f9 commit a37d707

File tree

2 files changed

+84
-3
lines changed

2 files changed

+84
-3
lines changed
 

‎variant/README.md

+54-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Circle variant
22

3-
[**variant code here**](variant.hxx)
3+
Browse implementation [**variant.hxx**](variant.hxx).
44

55
This is a Circle implementation of C++20's [`std::variant`](http://eel.is/c++draft/variant) class. The goal of this exercise isn't about providing a faster-compiling variant, although it it that. Like my [mdspan implementation](https://github.com/seanbaxter/mdspan#mdspan-circle), working through variant is an opportunity to extend the language so that writing such advanced code no longer poses a challenge.
66

@@ -317,6 +317,58 @@ The trailing expand operator `...` substitutes the predicate for each pack eleme
317317
318318
## Visit
319319
320+
By far the most troublesome function in the C++ variant is its [visit](http://eel.is/c++draft/variant.visit) function. This function is tasked with multi-dimensional control flow, invoking an argument callable with the active variant members extracted from a parameter of variant arguments. Michael Park [documents](https://mpark.github.io/programming/2019/01/22/variant-visitation-v2/) the ongoing struggles of implementing even a one-dimensional visitor.
321+
322+
The Circle builtins `__visit` and `__visit_r` (which adds an explicit return type, see [visit<R>: Explicit Return Type for `visit`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0655r1.pdf)) generate n-dimensional control flow to map runtime variables to n constants, exposed as a parameter pack named `indices`. The user supplies an expression which is substituted for each combination of constants.
323+
324+
[**enum.cxx**](enum.cxx) [Compiler explorer](https://godbolt.org/z/n9f4er9ss)
325+
326+
```cpp
327+
#include <utility>
328+
#include <iostream>
329+
330+
enum shapes_t {
331+
circle, square, rhombus = 20, trapezoid, triangle, ellipse
332+
};
333+
334+
template<int I, int J, shapes_t z>
335+
void f() {
336+
std::cout<< I<< " "<< J<< " "<< z.string<< "\n";
337+
}
338+
339+
int main() {
340+
341+
// Invoke with an element from a interval.
342+
constexpr int XDim = 10;
343+
int x = 3;
344+
345+
// Invoke with an element from a collection.
346+
using YSequence = std::index_sequence<1, 3, 5, 7, 9>;
347+
int y = 7;
348+
349+
// Invoke with an element from an enumeration.
350+
shapes_t z = rhombus;
351+
352+
__visit<XDim, YSequence, shapes_t>(
353+
f<indices...>(),
354+
x, y, z
355+
);
356+
}
357+
```
358+
```
359+
$ circle enum.cxx && ./enum
360+
3 7 rhombus
361+
```
362+
363+
`__visit` is pretty generic. It supports three kinds of parameterizations:
364+
1. Specify a constant for the right-hand side of an interval. Passing an integral N generates control flow for integral values between 0 and N-1. This is all we need for a variant visit, where the dimensions are provided by `variant_size_v`.
365+
2. Specify a collection of integral/enum types in an `std::integer_sequence` container. These constants don't have to be ordered.
366+
3. Specify an enumeration type. Reflection causes each enumerator in the type to be visited.
367+
368+
The builtin takes N template arguments, one for each dimension, and N + 1 function arguments. The first function argument is a dependent expression that's substituted for each combination of constant values. The `indices` pack declaration is visible only in this context. The subsequent N function arguments are integral or enum values bounded by their corresponding template arguments.
369+
370+
Note that the visitor expression is not put into a lambda. There is no closure. In the code above, the expression is substituted 10 * 5 * 6 = 300 times, all in the scope of the `main` function. Making n-dimensional visitation a builtin allows the compiler to most efficiently implement this intricate control flow.
371+
320372
```cpp
321373
template <class Visitor, class... Variants>
322374
constexpr decltype(auto) visit(Visitor&& vis, Variants&&... vars) {
@@ -330,5 +382,4 @@ constexpr decltype(auto) visit(Visitor&& vis, Variants&&... vars) {
330382
}
331383
```
332384
333-
334-
385+
The variant `std::visit` function now has a trivial implementation. We return the result of a call to `__visit`, and that's all. The n-dimensional collection of variant sizes is expressed with `variant_size_v<Variants.remove_reference>...`. The corresponding collection of runtime index values is expressed with `vars.index()...`. The result expression extracts each combination of indices by calling the `get` member function on each forwarded variant (to maintain its value category), passing the implicitly-declared `indices` pack as the `get` template argument, and expanding this pack in the callable's function argument list.

‎variant/enum.cxx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <utility>
2+
#include <iostream>
3+
4+
enum shapes_t {
5+
circle, square, rhombus = 20, trapezoid, triangle, ellipse
6+
};
7+
8+
template<int I, int J, shapes_t z>
9+
void f() {
10+
std::cout<< I<< " "<< J<< " "<< z.string<< "\n";
11+
}
12+
13+
int main() {
14+
15+
// Invoke with an element from a interval.
16+
constexpr int XDim = 10;
17+
int x = 3;
18+
19+
// Invoke with an element from a collection.
20+
using YSequence = std::index_sequence<1, 3, 5, 7, 9>;
21+
int y = 7;
22+
23+
// Invoke with an element from an enumeration.
24+
shapes_t z = rhombus;
25+
26+
__visit<XDim, YSequence, shapes_t>(
27+
f<indices...>(),
28+
x, y, z
29+
);
30+
}

0 commit comments

Comments
 (0)
Please sign in to comment.