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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
# sfinae
Substitution Failure Is Not An Error. everything else is.
a toy language which takes' C++ template metaprogramming to another level.
## build
make release build, ./sfinae in the root, objects in bin/
make debug rebuild with address and undefined sanitizers
make check run every program in examples/ and tutorial/
make format clang-format the sources
make lint clang-tidy naming check
make clean
make install copy ./sfinae to PREFIX/bin, PREFIX defaults to /usr/local
make uninstall
## run
./sfinae examples/hello.sfinae
## learning it
six lessons under tutorial/, each a program that runs and explains itself in
its comments. read them in order:
01_hello.sfinae there are no strings, a string is a tuple of characters
02_numbers.sfinae there are no numbers, a number is the distance between two characters
03_variables.sfinae a variable holds a type, and there is no assignment
04_conditions.sfinae nothing is a condition until you say ::value
05_loops.sfinae ::repeat, and counting with the undeclare dance
06_functions.sfinae functions take types, return types, and see only their parameters
examples/ has more, including fib(0) to fib(10).
## the rules
- every notion starts with `::` and every statement ends with `::commit;`.
nothing happens until it is committed
- there are no numeric literals. a digit is a lexer error. the only way to get
a number is to subtract two characters: `::difference<'F', 'A'>` is 5
- there are no strings. a string is a `::tuple` of `::integral_constant<::char, N>`
types, one per character, each its own distance from `'\0'`
- names are lists of characters: `::name<'f', 'i', 'b'>`, at most 15 of them
- a variable holds a type, and the type must be a `::decltype(...)` of a value or
it cannot be declared
- reassignment does not exist. `::undeclare` it, then `::declare` it again
- conditions are not conditions until you say `::value`
- loop, if and function bodies are a `::sequence<...>` of committed statements,
separated by commas
- functions take types, return types, and see nothing but their parameters
- comments are `//` to the end of the line
## statements
::declare<NAME, TYPE>::commit;
::undeclare<NAME>::commit;
::emit<TYPE>::commit; prints what the type says, then a newline
::repeat<VALUE, SEQUENCE>::commit;
::enable_if_t<COND, SEQUENCE>::commit;
::static_assert<COND>::commit; fails at runtime
::define<NAME, ::parameters<NAME, ...>, SEQUENCE>::commit;
::return<TYPE>::commit; only inside a function
## types
::void ::int32_t ::byte ::size_t ::char
::remove_cv_t<TYPE> does nothing
::array<TYPE, VALUE> an array type with an extent
::conditional_t<COND, TYPE, TYPE>
::decltype(VALUE) the only type that carries a value
::integral_constant<::char, VALUE> a character
::tuple<TYPE, ...> says what its elements say, in order
::invoke_result_t<NAME, ::arguments<TYPE, ...>> a call
## values
::difference<'c', 'c'> the one and only literal
::sum<V, V> ::product<V, V> ::quotient<V, V>
::value_of<NAME>
::extent<TYPE>::value an array's extent, 0 otherwise
::tuple_size<TYPE>::value same thing, longer
::integral_constant<::size_t, V>::value also does nothing
::conditional<COND, V, V>
::declval<TYPE>() the value inside a decltype
## conditions
::true_type::value ::false_type::value
::is_same<TYPE, TYPE>::value
::equal<V, V>::value ::less<V, V>::value
## what emit prints
::decltype(V) the number
::integral_constant<::char, V> the character
::tuple<...> its elements, glued together
::array<T, N> array[N]
::void nothing, not even a newline
anything else the type's name
## license
MIT, see LICENSE.
|