diff options
| author | hachem <im@hachem.wtf> | 2026-09-19 22:04:44 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-19 22:04:44 +0200 |
| commit | 2da2c56c72e7085bd9a52f848df90c6fc701de7f (patch) | |
| tree | 96cf837a606297aed921b9adaac783940a1aa7cd /tutorial | |
| parent | 6487b5b5301f6cb37c95c3571578d5a42adb2a1a (diff) | |
docs: add tutorial
Diffstat (limited to 'tutorial')
| -rw-r--r-- | tutorial/01_hello.sfinae | 24 | ||||
| -rw-r--r-- | tutorial/02_numbers.sfinae | 61 | ||||
| -rw-r--r-- | tutorial/03_variables.sfinae | 46 | ||||
| -rw-r--r-- | tutorial/04_conditions.sfinae | 74 | ||||
| -rw-r--r-- | tutorial/05_loops.sfinae | 47 | ||||
| -rw-r--r-- | tutorial/06_functions.sfinae | 118 |
6 files changed, 370 insertions, 0 deletions
diff --git a/tutorial/01_hello.sfinae b/tutorial/01_hello.sfinae new file mode 100644 index 0000000..81d84e6 --- /dev/null +++ b/tutorial/01_hello.sfinae @@ -0,0 +1,24 @@ +// hello, world +// +// there are no strings. there are no string literals. there is ::tuple, a type +// made of other types, and ::integral_constant<::char, N>, a type that happens +// to be a character. ::emit prints whatever a type has to say and then a newline +// there are also no numbers, so every character is its own distance from '\0' + +::emit< + ::tuple< + ::integral_constant<::char, ::difference<'H', '\0'>>, + ::integral_constant<::char, ::difference<'e', '\0'>>, + ::integral_constant<::char, ::difference<'l', '\0'>>, + ::integral_constant<::char, ::difference<'l', '\0'>>, + ::integral_constant<::char, ::difference<'o', '\0'>>, + ::integral_constant<::char, ::difference<',', '\0'>>, + ::integral_constant<::char, ::difference<' ', '\0'>>, + ::integral_constant<::char, ::difference<'w', '\0'>>, + ::integral_constant<::char, ::difference<'o', '\0'>>, + ::integral_constant<::char, ::difference<'r', '\0'>>, + ::integral_constant<::char, ::difference<'l', '\0'>>, + ::integral_constant<::char, ::difference<'d', '\0'>>, + ::integral_constant<::char, ::difference<'!', '\0'>> + > +>::commit; diff --git a/tutorial/02_numbers.sfinae b/tutorial/02_numbers.sfinae new file mode 100644 index 0000000..76f7dca --- /dev/null +++ b/tutorial/02_numbers.sfinae @@ -0,0 +1,61 @@ +// lesson 2: numbers +// +// a digit is a syntax error. the only number literal is ::difference<'a', 'b'>, +// the distance between two characters. 'F' - 'A' is 5. ::emit takes a type, and a +// value is not a type, so wrap it in ::decltype(...) to make it one + +::emit<::decltype(::difference<'F', 'A'>)>::commit; + +// arithmetic is ::sum, ::product and ::quotient. +// no operators +// no precedence +// no problem + +::emit< + ::decltype( + ::sum< + ::difference<'F', 'A'>, + ::difference<'C', 'A'> + > + ) +>::commit; + +::emit< + ::decltype( + ::product< + ::difference<'D', 'A'>, + ::difference<'E', 'A'> + > + ) +>::commit; + +::emit< + ::decltype( + ::quotient< + ::difference<'H', 'A'>, + ::difference<'C', 'A'> + > + ) +>::commit; + +// ::integral_constant<::size_t, V>::value is V. it exists so you can write more +// ::extent<T>::value is the length of an array type, and 0 for anything else +// this is the long way to write 20 + +::emit< + ::decltype( + ::extent< + ::array< + ::byte, + ::integral_constant< + ::size_t, + ::difference<'Z', 'F'> + >::value + > + >::value + ) +>::commit; + +// emitting a type that is not a value prints the name of the type + +::emit<::remove_cv_t<::int32_t>>::commit; diff --git a/tutorial/03_variables.sfinae b/tutorial/03_variables.sfinae new file mode 100644 index 0000000..6f200f1 --- /dev/null +++ b/tutorial/03_variables.sfinae @@ -0,0 +1,46 @@ +// lesson 3: variables +// +// a name is a list of characters. a variable holds a type, not a value, and the +// only type that carries a value is ::decltype(...). the ::conditional_t around +// it is not required. it is just what everyone does + +::declare< + ::name<'x'>, + ::conditional_t< + ::true_type::value, + ::decltype(::difference<'F', 'A'>), + ::void + > +>::commit; + +// reading it back is ::value_of<NAME>, which is a value, so ::decltype it again + +::emit<::decltype(::value_of<::name<'x'>>)>::commit; + +::declare< + ::name<'y'>, + ::decltype( + ::sum< + ::value_of<::name<'x'>>, + ::difference<'B', 'A'> + > + ) +>::commit; + +::emit<::decltype(::value_of<::name<'y'>>)>::commit; + +// there is no assignment. to change x, undeclare it and declare it again + +::undeclare<::name<'x'>>::commit; + +::declare< + ::name<'x'>, + ::decltype( + ::product< + ::value_of<::name<'y'>>, + ::difference<'C', 'A'> + > + ) +>::commit; + +::emit<::decltype(::value_of<::name<'x'>>)>::commit; diff --git a/tutorial/04_conditions.sfinae b/tutorial/04_conditions.sfinae new file mode 100644 index 0000000..94e0cbf --- /dev/null +++ b/tutorial/04_conditions.sfinae @@ -0,0 +1,74 @@ +// lesson 4: conditions +// +// a condition is ::true_type::value, ::false_type::value, ::equal<V, V>::value, +// ::less<V, V>::value, or ::is_same<T, T>::value. the ::value is mandatory +// a condition without it is a type, and a type is not a condition. + +// ::conditional_t<C, T, T> picks a type. this prints 1 + +::emit< + ::conditional_t< + ::true_type::value, + ::decltype(::difference<'B', 'A'>), + ::void + > +>::commit; + +// this picks ::void, and emitting ::void prints nothing at all. not even a newline + +::emit< + ::conditional_t< + ::less< + ::difference<'B', 'A'>, + ::difference<'A', 'A'> + >::value, + ::decltype(::difference<'Z', 'A'>), + ::void + > +>::commit; + +// ::enable_if_t<C, ::sequence<...>> runs the statements when C holds +// the statements inside a sequence are separated by commas, and each one is +// still committed on its own + +::enable_if_t< + ::is_same< + ::remove_cv_t<::int32_t>, + ::int32_t + >::value, + ::sequence< + ::emit< + ::tuple< + ::integral_constant<::char, ::difference<'s', '\0'>>, + ::integral_constant<::char, ::difference<'a', '\0'>>, + ::integral_constant<::char, ::difference<'m', '\0'>>, + ::integral_constant<::char, ::difference<'e', '\0'>> + > + >::commit + > +>::commit; + +// ::static_assert<C> stops the program when C does not hold. it does this at +// runtime, because that is when we know + +::static_assert< + ::equal< + ::difference<'C', 'A'>, + ::sum< + ::difference<'B', 'A'>, + ::difference<'B', 'A'> + > + >::value +>::commit; + +// ::conditional<C, V, V> is the same idea for values. this prints 9 + +::emit< + ::decltype( + ::conditional< + ::false_type::value, + ::difference<'A', 'A'>, + ::difference<'J', 'A'> + > + ) +>::commit; diff --git a/tutorial/05_loops.sfinae b/tutorial/05_loops.sfinae new file mode 100644 index 0000000..d974f99 --- /dev/null +++ b/tutorial/05_loops.sfinae @@ -0,0 +1,47 @@ +// lesson 5: loops +// +// ::repeat<V, ::sequence<...>> runs the sequence V times. that is the whole +// loop. there is no index, no condition, no break. + +::repeat< + ::difference<'D', 'A'>, + ::sequence< + ::emit< + ::tuple< + ::integral_constant<::char, ::difference<'h', '\0'>>, + ::integral_constant<::char, ::difference<'i', '\0'>> + > + >::commit + > +>::commit; + +// counting needs a variable, and changing a variable needs the undeclare dance, +// and the dance needs a second variable to hold the value while the first one +// is dead. this prints 1, 2, 3 + +::declare< + ::name<'i'>, + ::decltype(::difference<'B', 'A'>) +>::commit; + +::repeat< + ::difference<'D', 'A'>, + ::sequence< + ::emit<::decltype(::value_of<::name<'i'>>)>::commit, + ::declare< + ::name<'n', 'e', 'x', 't'>, + ::decltype( + ::sum< + ::value_of<::name<'i'>>, + ::difference<'B', 'A'> + > + ) + >::commit, + ::undeclare<::name<'i'>>::commit, + ::declare< + ::name<'i'>, + ::decltype(::value_of<::name<'n', 'e', 'x', 't'>>) + >::commit, + ::undeclare<::name<'n', 'e', 'x', 't'>>::commit + > +>::commit; diff --git a/tutorial/06_functions.sfinae b/tutorial/06_functions.sfinae new file mode 100644 index 0000000..a85e5fa --- /dev/null +++ b/tutorial/06_functions.sfinae @@ -0,0 +1,118 @@ +// lesson 6: functions +// +// ::define<NAME, ::parameters<NAME, ...>, ::sequence<...>> defines a function +// a function sees nothing but its parameters. it gives back a type with +// ::return<TYPE>, or ::void if it never does. calling it is a type too: +// ::invoke_result_t<NAME, ::arguments<TYPE, ...>>, and every argument must be +// a ::decltype so it carries a value + +::define< + ::name<'s', 'q', 'u', 'a', 'r', 'e'>, + ::parameters<::name<'x'>>, + ::sequence< + ::return< + ::decltype( + ::product< + ::value_of<::name<'x'>>, + ::value_of<::name<'x'>> + > + ) + >::commit + > +>::commit; + +// prints 25 + +::emit< + ::invoke_result_t< + ::name<'s', 'q', 'u', 'a', 'r', 'e'>, + ::arguments<::decltype(::difference<'F', 'A'>)> + > +>::commit; + +// a function can return anything a type can be, which includes a tuple of +// characters, which is the closest thing to a string you will get + +::define< + ::name<'g', 'r', 'e', 'e', 't'>, + ::parameters<>, + ::sequence< + ::return< + ::tuple< + ::integral_constant<::char, ::difference<'h', '\0'>>, + ::integral_constant<::char, ::difference<'e', '\0'>>, + ::integral_constant<::char, ::difference<'y', '\0'>> + > + >::commit + > +>::commit; + +::emit< + ::invoke_result_t< + ::name<'g', 'r', 'e', 'e', 't'>, + ::arguments<> + > +>::commit; + +// the result is a type. to use it as a value in arithmetic, ::declval<T>() gets +// the value back out. the parentheses are mandatory. it is a function, after all +// this prints 10 + +::emit< + ::decltype( + ::sum< + ::declval< + ::invoke_result_t< + ::name<'s', 'q', 'u', 'a', 'r', 'e'>, + ::arguments<::decltype(::difference<'D', 'A'>)> + > + >(), + ::difference<'B', 'A'> + > + ) +>::commit; + +// recursion works, because nothing stopped it. factorial of 5 + +::define< + ::name<'f', 'a', 'c', 't'>, + ::parameters<::name<'n'>>, + ::sequence< + ::enable_if_t< + ::less< + ::value_of<::name<'n'>>, + ::difference<'B', 'A'> + >::value, + ::sequence< + ::return<::decltype(::difference<'B', 'A'>)>::commit + > + >::commit, + ::return< + ::decltype( + ::product< + ::value_of<::name<'n'>>, + ::declval< + ::invoke_result_t< + ::name<'f', 'a', 'c', 't'>, + ::arguments< + ::decltype( + ::sum< + ::value_of<::name<'n'>>, + ::difference<'A', 'B'> + > + ) + > + > + >() + > + ) + >::commit + > +>::commit; + +::emit< + ::invoke_result_t< + ::name<'f', 'a', 'c', 't'>, + ::arguments<::decltype(::difference<'F', 'A'>)> + > +>::commit; |
