// g++ -std=c++11 -Wall -Wextra -Wpedantic -o pass_by pass_by.cpp // Pass arguments by position or by name (typesafely, in C++11, without // template recursion). // This could be cleaned up a bit if C++11 were not a requirement: // - `std::integer_sequence` and friends are part of C++14. // - `std::conjunction` is part of C++17. // - `std::initializer_list` could be replaced by fold expressions in C++17. // - `std::*<>::value` could be replaced by `std::*_v<>` in C++17. // - `*_impl` could be internal templated lambdas in C++20. // TODO: Try to change `*_impl` into internal C++11 lambdas using `auto` and // `decltype`: <https://www.youtube.com/watch?v=FRkJCvHWdwQ&t=24m35s>. This // probably won't work because template parameter packs *are not types*. // TODO: Do we need to take `index_sequence` as function parameter? Can we not // take it as (explicit) template parameter? I've seen a talk on when to use // what, and why (in the context of `enable_if`). Look it up. #include <tuple> #include <utility> #include <iostream> // C++14 `std::index_sequence`, `std::make_index_sequence`. template<std::size_t... Is> struct index_sequence {}; template<std::size_t N, std::size_t... Is> struct make_index_sequence : make_index_sequence<N-1, N-1, Is...> {}; template<std::size_t... Is> struct make_index_sequence<0, Is...> : index_sequence<Is...> {}; // C++17 `std::conjunction`. template<typename...> struct conjunction : std::true_type {}; template<typename B1> struct conjunction<B1> : B1 {}; template<typename B1, typename... Bn> struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {}; // template<typename Key, typename Value> // void print(Key key, Value value) { // std::cout << key << ": " << value << std::endl; // } void print(int key, std::string value) { std::cout << key << ": " << value << std::endl; } void print(std::string key, std::string value) { std::cout << key << ": " << "(string) " << value << std::endl; } void print(std::string key, int value) { std::cout << key << ": " << "(int) " << value << std::endl; } template<std::size_t... Is, typename... Args> void pass_by_position_impl(index_sequence<Is...>, Args const &... args) { (void)std::initializer_list<int>{ (print(Is, args), 0) ... }; } template<std::size_t... Is, typename... Args> void pass_by_pair_impl(index_sequence<Is...>, std::pair<std::string, Args> const &... args) { (void)std::initializer_list<int>{ (print(args.first, args.second), 0) ... }; } template<std::size_t... Is, typename... Args> void pass_by_name_impl(index_sequence<Is...>, Args const &... args) { auto args_tuple = std::tie(args...); static_assert( sizeof...(Args) % 2 == 0, "pass_by_name takes an even number of arguments" ); static_assert( conjunction< std::is_convertible< decltype(std::get<2*Is+0>(args_tuple)), std::string > ... >::value, "pass_by_name even arguments must be convertible to std::string" ); (void)std::initializer_list<int>{ ( print( std::get<2*Is+0>(args_tuple), std::get<2*Is+1>(args_tuple) ), 0 ) ... }; } template<typename... Args> void pass_by_position(Args &&... args) { pass_by_position_impl(make_index_sequence<sizeof...(Args)>{}, args...); } template<typename... Args> void pass_by_pair(std::pair<std::string, Args> const &... args) { pass_by_pair_impl(make_index_sequence<sizeof...(Args)>{}, args...); } template<typename... Args> void pass_by_name(Args const &... args) { pass_by_name_impl(make_index_sequence<sizeof...(Args) / 2>{}, args...); } int main() { // 0: eeny // 1: meeny // 2: miny // 3: moe pass_by_position("eeny", "meeny", "miny", "moe"); // Hello: (string) world // Answer: (int) 42 pass_by_pair<std::string, int>({"Hello", "world"}, {"Answer", 42}); // Hello: (string) world // Answer: (int) 42 pass_by_name("Hello", "world", "Answer", 42); }