Wednesday, 24 July 2013

Named function arguments for C++(11)


Some time ago I wrote a post about emulating named parameters in C++. But in the meantime I discovered another possibility of low-cost emulation of that feature: just use the tags! Use what? OK, let's have an example. If you look up the C++14 proposal number N3554  "A Parallel Algorithms Library for C++" you'll see things like:
  std::sort(exec, vec.begin(), vec.end());

  // parallel sort with non-standard implementation-provided execution policies:
  std::sort(vectorize_in_this_thread, vec.begin(), vec.end());
  std::sort(submit_to_my_thread_pool, vec.begin(), vec.end());
  std::sort(execute_on_that_gpu, vec.begin(), vec.end());
  std::sort(offload_to_my_fpga, vec.begin(), vec.end());
  std::sort(send_this_computation_to_the_cloud, vec.begin(), vec.end());
You are right, execute_on_that_gpu and offload_to_my_fpga are tags! So how could we use that to name the arguments? What about:
  bool ok = funcXXX(1, 2, 3, delete_data, true);
OK, that's perhaps not that readable. A better idea were maybe:
  bool ok = funcXXX(1, 2, 3);              // default is delete_data == false
  bool ok = funcXXX(1, 2, 3, delete_data); // now just use an empty tag!
That would be arguably more in line with the new C++11 (or is it C++14?) look and feel, considering these examples from the standard:
  std::optional b{std::in_place, "1"};    // C++14: calls Big{"1"} in place (no moving)
  std::unique_lock l(m, std::defer_lock); // don't lock now!

  // use my_alloc(2) to allocate 10 ints in a vector in a tuple
  std::tuple<int, std::vector<int, my_alloc>, double> 
    t5(std::allocator_arg, my_alloc(2), 42, v,  -3.14);

  std::tuple<int, float> t(1, 3.14);
  std::pair<Foo, Foo> p1(t, t);  // call the Foo(std::tuple<int, float>) overload
  std::pair<Foo, Foo> p2(std::piecewise_construct, t, t); // call the Foo(int, float) overload
Seems to be a nice new pattern in the standard library, isn't it? So maybe the way ahead just using tags for bools to simulate named parameters? The more general attempt:
  bool ok = funcXXX(obj_id, 1, obj_value, 2, env_value, 3, replace_data);
could be also of merit but isn't that readable to the general public. On the other hand, the std::tuple constructor above uses it in that exact manner. Maybe a thing worth pondering about.