4

It is common for libraries to have types which return instances of themselves from member functions to encourage chaining calls. For example, nlohmann json:

auto my_data = my_json_object["first key"]["second key"];

Is there some way to call a member function using the contents of a parameter pack? e.g.:

template<class... Keys>
auto get_elem(json json, Keys... keys)
{
   return json([keys]...); // -> json[keys[0]][keys[1]]...[keys[N]]
}

auto my_data = get_elem(my_json_object, "first key", "second key");
xzaton jw
  • 41
  • 3
  • I know people have made a hack to use a fold expression with an arbitrary binary callable. That would work without recursion, but it's unfortunate that it isn't supported out of the box. Another alternative is to convert the pack to a tuple and make use of someone's tuple-fold implementation. Neither is fantastic without already having the option available in the project. – chris Feb 12 '23 at 22:58
  • @chris Do you have a link to the example of someone doing this with a fold expression? I don't want to make my compile times any worse. – xzaton jw Feb 13 '23 at 16:06
  • he gave you homework – user1095108 Feb 13 '23 at 16:11
  • 1
    @xzatonjw, [This question](https://stackoverflow.com/questions/27582862/fold-expressions-with-arbitrary-callable) has a couple answers that bring up the approach and someone's small library. The last alternative is, of course, the answer here where you manage mutable state directly like traditional procedural iteration. – chris Feb 13 '23 at 17:57

2 Answers2

3

The simplest there is:

template<typename A, class First>
auto get_elem(A json, First first) {
   return json[first];
}


template<typename A, class First, class... Keys>
auto get_elem(A json, First first, Keys... keys) {
   return get_elem(json[first], keys...);
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Or effectively the same thing using if constexpr https://godbolt.org/z/Y6dxnEbPT Not that your answer is wrong or anything, my comment is just for completeness. :) – alagner Feb 13 '23 at 00:51
2

KamilCuk's answer is the best, this one is just food for thought:

auto& get(auto& j, auto&& a, auto&& ...b)
{
  auto e(&j[a]);

  return ((e = &e->operator[](b)), ...), *e;
}
user1095108
  • 14,119
  • 9
  • 58
  • 116
  • If doing this, my untested thought is to use references for simplicity and start at the root to allow 0 arguments: `auto& e = j; return ((e = e[keys]), ...);` – chris Feb 13 '23 at 17:41
  • On second thought, my comment should probably be using a binary fold to handle 0 arguments properly: `(e, (e = e[keys]), ...)` – chris Feb 13 '23 at 17:59