Jason Rice
2018-08-30 16:42:55 UTC
Here is a write up for a language feature that I would like to have
considered:
https://gist.github.com/ricejasonf/84c11ac6bb093c1ea8c380a9a466d8cb
I'm attempting to implement it in Clang, but I would like to get some early
feedback.
I've already received some feedback from Louis Dionne and he encouraged me
to post it here.
Thanks!
# Parametric Expression
The primary purpose here is to improve compile-time performance by
providing a tool that augments existing
metaprogramming features in C++. It involves the transformation of
expressions.
Essentially this feature is meant to combine the power of function
templates with the performance of type
aliases. Function templates as we have them now are loaded with features
such as overloading, constraints,
type deduction, SFINAE, ect.. When called not only do we get an expensive
template instantiation but also
potentially large symbols and extraneous code generation all for functions
we wouldn't call otherwise
unless we expected the optimizer to inline them out anyways. With this tool
we want to give programmers
the ability to allow this inlining at an earlier stage, but the solution is
elegant enough to provide a
few additional bonus features.
Consider the following declaration syntax:
```cpp
using add(auto a, auto b) {
return a + b;
}
```
Here is a function-like declaration where the domain and codomain are
expressions.
Invoking it merely transforms the expression in the context of the site of
invocation.
```cpp
int main() {
return add(40, 2);
}
// the same as
int main() {
return 40 + 2;
}
```
This ability to transform expressions has the same power as type aliases,
but here we can work with
constructs that have run-time effects as seen in Fusion/Hana style
metaprogramming. Additionally,
since this is a language feature, we could pontentially see the same
compile-time performance that
Kvasir.Mpl has without the difficult continuation interface (and it works
on expressions!).
## Additional Bonus Features
Since the expressions maintain the context of the site of invocation we
also get constexpr parameters.
```cpp
using to_integral_constant(auto x) {
return std::integral_constant<int, x>{};
}
int main() {
constexpr int x = 0;
static_assert(std::is_same_v<std::integral_constant<int, 0>,
decltype(to_integral_constant(x)));
}
```
We also get arrays and string literals that do not decay.
```cpp
using id(auto x) {
return x;
}
int main() {
char const* foo = id("foo");
}
// the same as
int main() {
char const* foo = "foo";
}
```
To top it off, we also get a clean syntax for Perfect Forwarding
* This assumes that the input expression casts to an rvalue
* See the rules below for information on how value categories are handled
```cpp
template <typename F, typename X, typename Y>
decltype(auto) flip(F&& f, X&& x, y&& y) {
return std::forward<F>(f)(std::forward<Y>(y), std::forward<X>(x));
}
// becomes
using flip(auto f, auto x, auto y) {
return f(y, x);
}
```
## As a Member of a Class
Parametric expressions can be used as a member in conjunction with operator
overloading to create an
invocable object:
```cpp
struct id_fn {
using operator()(auto x) {
return x;
}
};
id_fn{}(42);
static_assert(std::is_invocable<id_fn, int>::value);
```
## The Rules
1. The input parameter list may contain only one parameter pack in any
position.
* This is afforded since we don't do type deduction
2. The input parameters may be unexpanded parameter packs.
* This adds a bit more power for working with lists.
* see [Multicategory][1]
3. The output expression must NOT contain any unexpanded parameter packs
* This would prevent pack expansion ambiguity at the call site
4. Input parameter type specifiers must be completely unconstrained and
never have type qualifiers.
* We could possibly apply constraints to the type in the future via
Concepts
5. The definition is a compound statement where the final statement must
either be a return statement
or a constexpr if/else statement where each branch follows this same
rule.
* There can be only one return statement which yields the output
expression
6. Recursion is not allowed
* Not sure if recursion would be feasible, but we could at least use
Odin Holmes' recursive
alias pattern
7. Input parameters are expression aliases which follow different rules
with regard to the evaluated
type's value category and reference type:
1. Prvalue constant-expressions are simply pasted wherever it is used
in the definition.
* This affords constexpr parameters and literals
2. Unexpanded parameter packs are pasted where the parameter is used in
the expression.
* Again note that they must be expanded wherever they are used in
the definition
2. Everything else is bound to an lvalue reference within the compound
statement implicitly.
3. Expressions evaluating to an rvalue have the last evaluated use in
the definition cast to an
rvalue.
* The compiler should be able to determine this internally even for
cases where order of
evaluation is unspecified.
* This also implies that this could be used to detect order of
evaluation of a function call.
* This is similar to how Rust handles implicit moves!
8. Input expressions that are not constant-expressions will be evaluated
immediately from left to right
as specified in the parameter list.
* Note that unexpanded packs are not included in this
9. The output is a generated compound statement evaluated as an expression
that is specified using
the return statement in the definition body of the parametric expression.
I see this as potentially having a huge impact on the ability to implement
ranges, expression templates,
metaprogramming libraries, and my own pet case of nesting continuations for
various purposes. It would be
really cool to get this in C++20 if it is not too late.
Thanks for looking at this!
Jason Rice
[1]: https://en.wikipedia.org/wiki/Multicategory
considered:
https://gist.github.com/ricejasonf/84c11ac6bb093c1ea8c380a9a466d8cb
I'm attempting to implement it in Clang, but I would like to get some early
feedback.
I've already received some feedback from Louis Dionne and he encouraged me
to post it here.
Thanks!
# Parametric Expression
The primary purpose here is to improve compile-time performance by
providing a tool that augments existing
metaprogramming features in C++. It involves the transformation of
expressions.
Essentially this feature is meant to combine the power of function
templates with the performance of type
aliases. Function templates as we have them now are loaded with features
such as overloading, constraints,
type deduction, SFINAE, ect.. When called not only do we get an expensive
template instantiation but also
potentially large symbols and extraneous code generation all for functions
we wouldn't call otherwise
unless we expected the optimizer to inline them out anyways. With this tool
we want to give programmers
the ability to allow this inlining at an earlier stage, but the solution is
elegant enough to provide a
few additional bonus features.
Consider the following declaration syntax:
```cpp
using add(auto a, auto b) {
return a + b;
}
```
Here is a function-like declaration where the domain and codomain are
expressions.
Invoking it merely transforms the expression in the context of the site of
invocation.
```cpp
int main() {
return add(40, 2);
}
// the same as
int main() {
return 40 + 2;
}
```
This ability to transform expressions has the same power as type aliases,
but here we can work with
constructs that have run-time effects as seen in Fusion/Hana style
metaprogramming. Additionally,
since this is a language feature, we could pontentially see the same
compile-time performance that
Kvasir.Mpl has without the difficult continuation interface (and it works
on expressions!).
## Additional Bonus Features
Since the expressions maintain the context of the site of invocation we
also get constexpr parameters.
```cpp
using to_integral_constant(auto x) {
return std::integral_constant<int, x>{};
}
int main() {
constexpr int x = 0;
static_assert(std::is_same_v<std::integral_constant<int, 0>,
decltype(to_integral_constant(x)));
}
```
We also get arrays and string literals that do not decay.
```cpp
using id(auto x) {
return x;
}
int main() {
char const* foo = id("foo");
}
// the same as
int main() {
char const* foo = "foo";
}
```
To top it off, we also get a clean syntax for Perfect Forwarding
* This assumes that the input expression casts to an rvalue
* See the rules below for information on how value categories are handled
```cpp
template <typename F, typename X, typename Y>
decltype(auto) flip(F&& f, X&& x, y&& y) {
return std::forward<F>(f)(std::forward<Y>(y), std::forward<X>(x));
}
// becomes
using flip(auto f, auto x, auto y) {
return f(y, x);
}
```
## As a Member of a Class
Parametric expressions can be used as a member in conjunction with operator
overloading to create an
invocable object:
```cpp
struct id_fn {
using operator()(auto x) {
return x;
}
};
id_fn{}(42);
static_assert(std::is_invocable<id_fn, int>::value);
```
## The Rules
1. The input parameter list may contain only one parameter pack in any
position.
* This is afforded since we don't do type deduction
2. The input parameters may be unexpanded parameter packs.
* This adds a bit more power for working with lists.
* see [Multicategory][1]
3. The output expression must NOT contain any unexpanded parameter packs
* This would prevent pack expansion ambiguity at the call site
4. Input parameter type specifiers must be completely unconstrained and
never have type qualifiers.
* We could possibly apply constraints to the type in the future via
Concepts
5. The definition is a compound statement where the final statement must
either be a return statement
or a constexpr if/else statement where each branch follows this same
rule.
* There can be only one return statement which yields the output
expression
6. Recursion is not allowed
* Not sure if recursion would be feasible, but we could at least use
Odin Holmes' recursive
alias pattern
7. Input parameters are expression aliases which follow different rules
with regard to the evaluated
type's value category and reference type:
1. Prvalue constant-expressions are simply pasted wherever it is used
in the definition.
* This affords constexpr parameters and literals
2. Unexpanded parameter packs are pasted where the parameter is used in
the expression.
* Again note that they must be expanded wherever they are used in
the definition
2. Everything else is bound to an lvalue reference within the compound
statement implicitly.
3. Expressions evaluating to an rvalue have the last evaluated use in
the definition cast to an
rvalue.
* The compiler should be able to determine this internally even for
cases where order of
evaluation is unspecified.
* This also implies that this could be used to detect order of
evaluation of a function call.
* This is similar to how Rust handles implicit moves!
8. Input expressions that are not constant-expressions will be evaluated
immediately from left to right
as specified in the parameter list.
* Note that unexpanded packs are not included in this
9. The output is a generated compound statement evaluated as an expression
that is specified using
the return statement in the definition body of the parametric expression.
I see this as potentially having a huge impact on the ability to implement
ranges, expression templates,
metaprogramming libraries, and my own pet case of nesting continuations for
various purposes. It would be
really cool to get this in C++20 if it is not too late.
Thanks for looking at this!
Jason Rice
[1]: https://en.wikipedia.org/wiki/Multicategory
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/95ae303b-329c-4121-aa5f-69edf040d5b5%40isocpp.org.
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/95ae303b-329c-4121-aa5f-69edf040d5b5%40isocpp.org.