I am trying to do something involving CTAD within function calls and it's best to first explain the desired behavior with an example, before asking my questions. NB: the below is a bit of a simplification of how I want the end thing to look.
namespace args {
template <typename...>
struct Required {};
template <typename...>
struct Optional {};
template <
typename RequiredParameters,
typename OptionalParameters,
typename... Params>
struct ParameterSet
{
template <typename ...Args>
constexpr ParameterSet(Args&&... args)
: params_{std::forward_as_tuple(std::forward<Args>(args)...)} { }
std::tuple<Params...> params_;
};
} // namespace args
template <typename... Params>
void foo(args::ParameterSet<Required<int, bool>, Optional<std::string>, Params...> params)
{
// do stuff
}
int a = 5;
foo({true, a, std::string{"hello"}});
Summary of Requirements
So what I want from the stuff in namespace args
is:
- Be able to call the function with the arguments passed in any order.
- Facilitate defining a set of parameters that are required and optional for a function call. I am planning to have type checks that there aren't any unexpected arguments, all required arguments are present, etc.
- Embed those requirements in the function class signature, as opposed to a separate template parameter and using
std::enable_if_t
. - Be able to forward the value categories of the arguments within the storage tuple inside
ParameterSet
. - Support easy function calls with CATD being able to infer the whole type of
ParameterSet
, without needing to spell it out on each function call. - (missing here) The end thing I want is to be able to annotate each argument with a name, so something like
foo(nameForBoolArg{true}, nameForIntArg{5}, ...)
. In essence what keyword / named arguments provide in python.
Things Tried
It was pretty clear to me I need some sort of deduction guide that helps the compiler. I tried the below:
template <typename RequiredParameters, typename OptionalParameters, typename... Args>
ParameterSet(Args&&...) -> ParameterSet<RequiredParameters, OptionalParameters, Args...>;
Hoping it would help the compiler deduce the type of the Params...
pack within ParameterSet
from the initialization used in the function call. However, I realized that I am mixing up 2 independent deduction contexts: the one for CTAD and the one for function parameter type deduction. In other words, the fact I have declared foo
with
args::ParameterSet<Required<int, bool>, Optional<std::string>, Params...>
doesn't mean the call to foo would be able to infer that I want to have Required<int, bool>
and Optional<std::string>
for the first 2 template parameters of ParameterSet
.
I banged my head for a few hours and at this point I am not sure what I want is possible. Would appreciate some help!
void foo(bool, int);
and then make overloads for the optionals - or let them take default values. Your current setup seems utterly complex for no reason.void foo(auto&&... args)
in that case, but I would like to embed into the signature directly, whether an argument is optional / required. The intention is to have some type checks as well to verify all required arguments are present, no non-expected arguments are present, etc.suddenly limiting the uses to only have arguments of different types
<-- I did explain in one of the bullet points I want to have tags, as opposed to just types. I thought that including that in the code would just overload it, but I want the eventual calling pattern to look like:foo(nameForBoolArg{true}, nameForIntArg{5}, ...)
, withnameForBoolArg
andnameForIntArg
representing named arguments. Those themselves are types, defined separately and implementing a concept that is unspecified - e.g. "NamedArgument".