Boost logo

Boost :

Subject: Re: [boost] [yap] Review part 1: documentation
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2018-02-13 18:30:19


AMDG

On 02/13/2018 09:44 AM, Zach Laine wrote:
> On Mon, Feb 12, 2018 at 10:13 PM, Steven Watanabe via Boost <
> boost_at_[hidden]> wrote:
>
>> On 02/12/2018 06:09 PM, Zach Laine wrote:
>>> On Mon, Feb 12, 2018 at 12:08 PM, Steven Watanabe via Boost <
>>> boost_at_[hidden]> wrote:
>>>
>>>> <snip>
>>>> Unwrapping the terminals causes surprising behavior,
>>>> which is only sort of fixed by automatically applying
>>>> the terminal transform and making transform a no-op
>>>> for non-Expressions.
>
>
> I guess I was reading too fast. I missed or misread this last sentence
> entirely:
>
>
>> In particular, if the terminal
>>>> transform returns an Expression, that Expression will
>>>> get processed again.
>>
>
> I get what you're concerned about now. That's not an issue. An expression
> is processed at most once by transform().
> > If, during a call to transform(), an expression E matches the
> transform-object TO, the result of TO(E) is used in the output of
> transform(). If, in the TO(E) call, its children are processed in some
> way, it is the user's responsibility not to infinitely recurse.
> transform() will copy or move the partial result TO(E), but will never
> revisit/re-transform it.
>
> [snip]
>
> Unless I'm misunderstanding something, the following
>> (seemingly reasonable) code will blow up the stack with
>> infinite recursion:
>>
>> int check_int(int i)
>> {
>> assert(i >= -42 && i < 42);
>> return i;
>> }
>>
>> int checked_add(int i, int j)
>> {
>> return check_int(i + j);
>> }
>>
>> struct checked_call
>> {
>> template<class F>
>> int operator()(F f, int i)
>> {
>> return check_int(f(i));
>> }
>> };
>>
>> struct int_checker
>> {
>> auto operator()(terminal_tag, placeholder<I>)
>> {
>> return make_expression<expr_kind::call>(check_int,
>> placeholder<I>{});
>> }
>> template<class L, class R>
>> auto operator()(plus_tag, L && lhs, R && rhs)
>> {
>> return make_expression<expr_kind::call>(checked_add,
>> transform(lhs, *this), transform(rhs, *this));
>> }
>> template<class F, class E>
>> auto operator()(call_tag, F&& f, E&&... e)
>> {
>> return make_expression<expr_kind::call>(checked_call{},
>> f, transform(e, *this)...);
>> }
>> };
>>
>> abs_ = make_terminal((int(*)(int))&std::abs);
>>
>> evaluate(transform(abs_(1_p + 2_p), int_checker{}), -5, 20);
>
>
> Let's look at the call graph for this:
>
> First transform() will call operator()(call_tag, ...) on abs_(1_p + 2_p).
> The result is make_expression<expr_kind::call>(checked_call{}, abs_,
> transform(1_p + 2_p), *this).
>
> To get that result, transform() will call (a *new* call to transform() at
> the user's request, not as part of the top-level call): operator()(plus_tag,
> ...) on 1_p and 2_p. The result is make_expression<expr_kind::
> call>(checked_add{}, transform(1_p, *this), transform(2_p, *this)).
>

  I think you're forgetting that the terminal transform is
applied before calling operator()(xxx_tag, ...).
Thus, the result is actually:

make_expression<expr_kind::call>(checked_add{},
  transform(transform(1_p, *this), *this),
  transform(transform(2_p, *this), *this)).

  Since the extra transform tacks on another call expr,
we end up up coming right back around to operator()(call_tag).

  The bottom line is, any transform which is not idempotent
for terminals is badly broken if you unwrap terminals
automatically.

  If you don't apply the terminal transform, but still
unwrap terminals, then it's *still* broken, because
then the terminal transform gets skipped entirely.

  Actually, the code I posted fails to compile (even after
fixing the obvious typos) because the recursion makes
it impossible for auto to deduce the return type.

> Just following the left side: to evaluate that, transform() (again, a *new*
> call): operator()(terminal_tag, 1_p), which returns
> make_expression<expr_kind::call>(check_int, 1_p).
>
> The right does something very similar, just with 2_p.
>
> No infinite recursion.
>

In Christ,
Steven Watanabe


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk