Boost hana first successful sfinae

180 Views Asked by At

I'd like to execute the first lambda in a list of lambdas, which isn't sfinaed out with boost hana. I have succeeded doing that with hana::if_ but that doesn't scale to any number of lambdas. I think I want lazy evaluation of find_if but I don't seem to grasp the documentation of hana well enough to find the right syntax. This is where you could tell me I am trying to solve it the wrong way or help me find a syntax that allows me to evaluate lazily.

You can find a live example with all of the code from this question on Godbolt.

Example: Say I want to implement a generic addToContainer function

template <class T>
void addToContainerIf(T &container, const typename T::value_type &element) {
  auto optional_inserted =
      hana::sfinae([&element](auto &c) { return c.insert(end(c), element); });
  auto optional_push_backed = hana::sfinae([&element](auto &c) {
    c.push_back(element);
    return end(c)--;
  });
  hana::if_(
      hana::is_nothing(optional_inserted(container)),
      [&] { optional_push_backed(container); }, [] {});
}

This is supposed to behave as follows and it does:

void testIf() {
  std::vector<int> v{1, 2};
  std::vector<int> expected{1, 2, 3};
  addToContainerIf(v, 3);
  if (v.size() != expected.size()) {
    std::cout << "If implementation failed: Expected v to have "
              << expected.size() << " elements, but it has " << v.size()
              << std::endl;
  } else {
    std::cout << "If implementation succeeded." << std::endl;
}

Now I'd like to change addToContainerIf such that it uses a hana::tuple to find the first lambda that is hana::just:

template <class T>
void addToContainerTuple(T &container, const typename T::value_type &element) {
  auto optional_inserted =
      hana::sfinae([&element](auto &c) { return c.insert(end(c), element); });
  auto optional_push_backed = hana::sfinae([&element](auto &c) {
    c.push_back(element);
    return end(c)--;
  });
  hana::find_if(
      hana::transform(hana::make_tuple(optional_inserted, optional_push_backed),
                      [&](auto f) { return f(container); }),
      hana::is_just);
}

However this fails with

Value of: v
Expected: equals { 1, 2, 3 }
  Actual: { 1, 2, 3, 3 }

Because both insert and push_back exist on std::vector.

So, instead, I'd like hana to lazily evaluate the sfinaes.

I have tried the following, but I don't understand the documentation well enough to find the correct syntax for what I want to achieve.

This fails to compile:

template <class T>
void addToContainerTupleLazy(T &container, const typename T::value_type &element) {
  auto optional_inserted =
      hana::sfinae([&element](auto &c) { return c.insert(end(c), element); });
  auto optional_push_backed = hana::sfinae([&element](auto &c) {
    c.push_back(element);
    return end(c)--;
  });
  hana::eval(
      hana::find_if(
          hana::transform(hana::make_tuple(optional_inserted, optional_push_backed),
                          [&](auto f) { return hana::make_lazy(f)(container); }),
          hana::make_lazy(hana::is_just)
      )
  );
}

Please see the godbolt link for the compilation error.

1

There are 1 best solutions below

0
Jan15 On

I found a solution myself, by not using hana::sfinae, but instead hana::is_valid:

template <class T>
void addToContainerTupleLazy(T &container,
                             const typename T::value_type &element) {
  auto optional_inserted = [&element](auto &c) {
    return c.insert(end(c), element);
  };
  auto optional_push_backed = [&element](auto &c) {
    c.push_back(element);
    return end(c)--;
  };

  hana::transform(
      hana::find_if(hana::make_tuple(optional_inserted, optional_push_backed),
                    [&](auto f) { return hana::is_valid(f)(container); }),
      [&](auto f) { return f(container); });
}