0

Consider the following template function:

template<typename T1 = Type1, typename T2 = Type2, int X1 = DefaultX1, /* ...and so on */>
int foo(/* skipped */) { ... }

The key is that it is has a long list of template paramters, both type and non-type, all with default values.

Most users will want to call this function with only zero, one or a small number of template parameters overridden from the default. However, this is only convenient with the existing definition if the first parameter to be overridden is T1 - in any other case, they must explicitly list all the earlier parameters and their default value. E.g., to override X1:

int x = foo<Type1,Type2,42>(...);

I'd like a pattern that allows users to override zero or more template arguments and call the function without mentioning the arguments they don't override.

That is, its purpose is something like the builder pattern to avoid combinatorial explosion in the number of offered constructors, or to avoid the same problem with constructors that have many default arguments.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • Possible duplicate of [Named? parameters in templates, functions](https://stackoverflow.com/questions/4209326/named-parameters-in-templates-functions) – xskxzr May 26 '18 at 06:13
  • Half of that question duplicates this one, more or less. Unfortunately, the other part of the question asks about "named function parameters" and the accepted answer answers only that part (this is a risk of combining two questions into one). So I think a question specifically on the template argument side of things makes sense. Consider, for example, that the one answer so far, which seems to work pretty well, isn't mentioned in the other question. – BeeOnRope May 26 '18 at 06:17

1 Answers1

1

You could use a set of tag classes to provide names for each compile-time parameter and allow users to supply just the parameters they want to, and in any order:

// In your header file:
#include <type_traits>

namespace FooArgs {
    template <typename T> struct T1 {};
    template <typename T> struct T2 {};
    template <int N> struct X1 {};
    // ...

    namespace detail {
        template <typename Enable, class Matcher, unsigned int N, class... Tags>
        struct match_at_most_enable;
        template <class Matcher, unsigned int N>
        struct match_at_most_enable<void, Matcher, N>
            : std::true_type {};
        template <class Matcher, unsigned int N, class Tag1, class... Tags>
        struct match_at_most_enable<
            typename std::enable_if<!Matcher::template match<Tag1>::value>::type,
            Matcher, N, Tag1, Tags...>
            : match_at_most_enable<void, Matcher, N, Tags...>::type {};
        template <class Matcher, unsigned int N, class Tag1, class... Tags>
        struct match_at_most_enable<
            typename std::enable_if<Matcher::template match<Tag1>::value>::type,
            Matcher, N, Tag1, Tags...>
            : match_at_most_enable<void, Matcher, N-1, Tags...>::type {};
        template <class Matcher, class Tag1, class... Tags>
        struct match_at_most_enable<
            typename std::enable_if<Matcher::template match<Tag1>::value>::type,
            Matcher, 0, Tag1, Tags...>
            : std::false_type {};
        template <class Matcher, unsigned int N, class... Tags>
        using match_at_most = match_at_most_enable<void, Matcher, N, Tags...>;

        template <class... Tags> struct inherit_all : Tags... {};
        template <class... Tags>
        constexpr inherit_all<Tags...>* combine() { return nullptr; }

        template <template<typename> class TT, typename DfltType, class... Tags>
        struct get_type_helper {
            template <class Tag>
            struct match : std::false_type {};
            template <typename T>
            struct match<TT<T>> : std::true_type {};
            static_assert(match_at_most<get_type_helper, 1, Tags...>::value,
                "An argument tag was specified more than once");

            template <typename T>
            struct wrap { using type = T; };
            template <typename T>
            static wrap<T> select(TT<T>*);
            static wrap<DfltType> select(...);
            using type = typename decltype(select(combine<Tags...>()))::type;
        };
        template <template<typename> class TT, typename DfltType, class... Tags>
        using get_type = typename get_type_helper<TT, DfltType, Tags...>::type;

        template <typename T, template<T> class TT, T DfltValue, class... Tags>
        struct get_value_helper {
            template <class Tag>
            struct match : public std::false_type {};
            template <T Value>
            struct match<TT<Value>> : public std::true_type {};
            static_assert(match_at_most<get_value_helper, 1, Tags...>::value,
                "An argument tag was specified more than once");

            template <T Value>
            static constexpr T select(TT<Value>*) { return Value; }
            static constexpr T select(...) { return DfltValue; }
            static constexpr T value = select(combine<Tags...>());
        };
        // Note if using C++17 or later, get_value could be a
        // constexpr variable template instead of a function.
        template <typename T, template<T> class TT, T DfltValue, class... Tags>
        constexpr T get_value()
        { return get_value_helper<T, TT, DfltValue, Tags...>::value; }
    }
}

template <class... Tags>
int foo(/*params*/)
{
    using namespace FooArgs::detail;
    using T1 = get_type<FooArgs::T1, Type1, Tags...>;
    using T2 = get_type<FooArgs::T2, Type2, Tags...>;
    constexpr int X1 = get_value<int, FooArgs::X1, DefaultX1, Tags...>();
    // ...
}

// Example usage:
void bar() {
    int n = foo<FooArgs::X1<42>, FooArgs::T1<int>>();
}

Note everything in FooArgs::detail is pretty generic, so if you wanted to use this pattern with more than one set of tags, you could move all that to some other header file and make get_type and get_value available with some more descriptive names or in some appropriately named namespace.

aschepler
  • 70,891
  • 9
  • 107
  • 161