You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sequences whose constructor(s) are not constexpr can't always know their bounds at compile-time. What we would like is for Iterable and such concepts to provide defaults that also work for these runtime sequences. The difference between runtime sequences and compile-time sequences is that the is_empty method can only return a runtime bool for the former, while it returns an IntegralConstant for the latter. Actually, we're talking about compile-time and runtime Logicals, but the difference is thin.
This issue presents the current state of things concerning support for runtime Iterables.
Below is a list of attempts I made, when the library first started, to get the algorithms to work for both runtime and compile-time Iterables. This list is kept mostly as an archive. Since then, I think I have found a way of doing it by encapsulating recursion in the while_ construct. However, more research is needed to confirm this and to see how/if it would all fit together in the library.
Attempt 1
This works, but it requires a lot of repetition:
template <typename Index, typename Iterable_>
staticconstexprautoat_helper(Bool<true>, Index n, Iterable_ iterable)
{ returnhead(iterable); }
template <typename Index, typename Iterable_>
staticconstexprautoat_helper(Bool<false>, Index n, Iterable_ iterable)
{ returnat_helper(n == size_t<1>, n - size_t<1>, tail(iterable)); }
template <typename Index, typename Iterable_>
staticconstexprautoat_helper(bool cond, Index n, Iterable_ iterable) {
if (cond) returnhead(iterable);
elsereturnat_helper(n == size_t<1>, n - size_t<1>, tail(iterable));
}
template <typename Index, typename Iterable_>
staticconstexprautoat_impl(Index n, Iterable_ iterable)
{ returnat_helper(n == size_t<0>, n, iterable); }
Attempt 2
The obvious improvement is to use an external if_ that factors the repetition away. Unfortunately, it does not work.
constexprstruct_lazy_if {
template <typename Then, typename Else, typename ...Args>
constexprautooperator()(Bool<true>, Then t, Else, Args ...args) const
{ returnt(args...); }
template <typename Then, typename Else, typename ...Args>
constexprautooperator()(Bool<false>, Then, Else e, Args ...args) const
{ returne(args...); }
template <typename Then, typename Else, typename ...Args>
constexprautooperator()(bool cond, Then t, Else e, Args ...args) const {
if (cond) returnt(args...);
elsereturne(args...);
}
} lazy_if{};
template <typename Index, typename Iterable_>
staticconstexprautoat_impl(Index n, Iterable_ iterable) {
returnlazy_if(n == size_t<0>,
[](auto n, auto it) { returnhead(it); },
[](auto n, auto it) { returnat_impl(n - size_t<1>, tail(it)); },
n, iterable
);
}
Note that we pass Args... to the lazy_if because otherwise we would need to return two different lambdas from the same function in the bool case. I think the reason for it not working is because we introduce a new intermediate lambda
[](auto n, auto it) { returnat_impl(n - size_t<1>, tail(it)); },
in the then branch, which causes the compiler to hit the instantiation limit while trying to recursively instantiate at_impl when it instantiates the lambda.
Attempt 2.1
Using a different if_, but it still does not work.
constexprstruct_tri_if {
template <typename Then, typename Else, typename Maybe, typename ...Args>
constexprautooperator()(Bool<true> cond, Then t, Else, Maybe, Args ...args) const
{ returnt(cond, args...); }
template <typename Then, typename Else, typename Maybe, typename ...Args>
constexprautooperator()(Bool<false> cond, Then, Else e, Maybe, Args ...args) const
{ returne(cond, args...); }
template <typename Then, typename Else, typename Maybe, typename ...Args>
constexprautooperator()(bool cond, Then, Else, Maybe m, Args ...args) const
{ returnm(cond, args...); }
} tri_if{};
template <typename Index, typename Iterable_>
staticconstexprautoat_impl(Index n, Iterable_ iterable) {
returntri_if(n == size_t<0>,
[](auto cond, auto n, auto it) { returnhead(it); },
[](auto cond, auto n, auto it) { returnat_impl(n - size_t<1>, tail(it)); },
[](bool cond, Index n, Iterable_ it) {
return cond ? head(it) : at_impl(n - size_t<1>, tail(it));
},
n, iterable
);
}
Attempt 3
Here, I'm trying to use structs to make everything more explicit. Still does not work because the problem is when instantiating the inner apply function. See the comment below.
template <typename Cond, typename = void>
structat_helper;
template <typename Dummy>
structat_helper<Bool<true>, Dummy> {
template <typename Index, typename Iterable_>
staticconstexprautoapply(Bool<true>, Index n, Iterable_ iterable) {
returnhead(iterable);
}
};
template <typename Dummy>
structat_helper<Bool<false>, Dummy> {
template <typename Index, typename Iterable_>
staticconstexprautoapply(Bool<false>, Index n, Iterable_ iterable) {
return at_helper<decltype(n == size_t<1>)>::apply(
n == size_t<1>, n - size_t<1>, tail(iterable)
);
}
};
template <typename Dummy>
structat_helper<bool, Dummy> {
template <typename Index, typename Iterable_>
staticconstexprautoapply(bool cond, Index n, Iterable_ iterable) {
if (cond)
returnhead(iterable);
else// This triggers infinite recursive instantiations.// return at_helper<Bool<false>>::apply(false_, n, iterable);// This works. decltype(...) is actually bool.return at_helper<decltype(n == size_t<1>)>::apply(
n == size_t<1>, n - size_t<1>, tail(iterable)
);
}
};
template <typename Index, typename Iterable_>
staticconstexprautoat_impl(Index n, Iterable_ iterable) {
return at_helper<decltype(n == size_t<0>)>::apply(n == size_t<0>, n, iterable);
}
Attempt 4
It works, but it's not a solution. I basically use the preprocessor to generate some of the duplicate code.
Trying to factor out the branches into lambdas. This recursively instantiates stuff too.
BOOST_HANA_CONSTEXPR_LAMBDA auto true_branch = [](auto at_impl, auto n, auto iterable)
{ returnhead(iterable); };
BOOST_HANA_CONSTEXPR_LAMBDA auto false_branch = [](auto at_impl, auto n, auto iterable)
{ returnat_impl(n - size_t<1>, tail(iterable)); };
template <typename F, typename Index, typename Iterable_>
staticconstexprautoat_helper(F at_impl, Bool<true>, Index n, Iterable_ iterable)
{ returntrue_branch(at_impl, n, iterable); }
template <typename F, typename Index, typename Iterable_>
staticconstexprautoat_helper(F at_impl, Bool<false>, Index n, Iterable_ iterable)
{ returnfalse_branch(at_impl, n, iterable); }
template <typename F, typename Index, typename Iterable_>
staticconstexprautoat_helper(F at_impl, bool cond, Index n, Iterable_ iterable) {
if (cond) returntrue_branch(at_impl, n, iterable);
elsereturnfalse_branch(at_impl, n, iterable);
}
BOOST_HANA_CONSTEXPR_LAMBDA auto at_impl = fix(
[](auto at_impl, auto n, auto iterable)
{ returnat_helper(at_impl, n == size_t<0>, n, iterable); }
);
Attempt 6
Make everything explicit by using structs.
template <typename Condition, typename Index, typename Iterable_>
structat_helper;
template <typename AtHelper>
structfalse_branch {
template <typename Index, typename It>
staticconstexprautoapply(Index n, It it) {
returnAtHelper::apply(n == size_t<1>, n - size_t<1>, tail(it));
}
};
template <typename Index, typename Iterable_>
structat_helper<Bool<true>, Index, Iterable_> {
staticconstexprautoapply(Bool<true>, Index n, Iterable_ it)
{ returnhead(it); }
};
template <typename Index, typename Iterable_>
structat_helper<Bool<false>, Index, Iterable_> {
staticconstexprautoapply(Bool<false>, Index n, Iterable_ it) {
// This worksreturn false_branch<at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>>::apply(n, it);
// This works too.// return at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>::// apply(n == size_t<1>, n - size_t<1>, tail(it));
}
};
template <typename Index, typename Iterable_>
structat_helper<bool, Index, Iterable_> {
staticconstexprautoapply(bool cond, Index n, Iterable_ it) {
if (cond) returnhead(it);
// Surprisingly, using the false_branch<...> just like above// does not work here. WTF!elsereturn at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>::
apply(n == size_t<1>, n - size_t<1>, tail(it));
}
};
template <typename Index, typename Iterable_>
staticconstexprautoat_impl(Index n, Iterable_ iterable) {
return at_helper<decltype(n == size_t<0>), decltype(n), decltype(iterable)>::
apply(n == size_t<0>, n, iterable);
}
The text was updated successfully, but these errors were encountered:
While that's actually a noble cause, I think this is out of scope for Hana. I think it can be made to work, but it would require basically having different code for runtime and compile-time algorithms, if we want them to be as efficient as possible (at compile-time and at runtime). This is because we use constexpr computations and parameter pack expansions to achieve compile-time efficiency, while runtime efficiency is achieve completely differently. Closing this for now, with possibility for reopening in the far future. This would probably be a Hana 2.0.
Sequences whose constructor(s) are not constexpr can't always know their bounds at compile-time. What we would like is for
Iterable
and such concepts to provide defaults that also work for these runtime sequences. The difference between runtime sequences and compile-time sequences is that theis_empty
method can only return a runtimebool
for the former, while it returns anIntegralConstant
for the latter. Actually, we're talking about compile-time and runtime Logicals, but the difference is thin.This issue presents the current state of things concerning support for runtime Iterables.
Below is a list of attempts I made, when the library first started, to get the algorithms to work for both runtime and compile-time Iterables. This list is kept mostly as an archive. Since then, I think I have found a way of doing it by encapsulating recursion in the
while_
construct. However, more research is needed to confirm this and to see how/if it would all fit together in the library.Attempt 1
This works, but it requires a lot of repetition:
Attempt 2
The obvious improvement is to use an external
if_
that factors the repetition away. Unfortunately, it does not work.Note that we pass
Args...
to thelazy_if
because otherwise we would need to return two different lambdas from the same function in thebool
case. I think the reason for it not working is because we introduce a new intermediate lambdain the then branch, which causes the compiler to hit the instantiation limit while trying to recursively instantiate
at_impl
when it instantiates the lambda.Attempt 2.1
Using a different
if_
, but it still does not work.Attempt 3
Here, I'm trying to use structs to make everything more explicit. Still does not work because the problem is when instantiating the inner
apply
function. See the comment below.Attempt 4
It works, but it's not a solution. I basically use the preprocessor to generate some of the duplicate code.
Attempt 5
Trying to factor out the branches into lambdas. This recursively instantiates stuff too.
Attempt 6
Make everything explicit by using structs.
The text was updated successfully, but these errors were encountered: