GuyGreer GuyGreer - 1 month ago 8
C++ Question

Does this method already have a name?

I have found that sometimes functions have very many parameters. A great many of these parameters will be optional and sometimes a group of these options often come from a single other object (so you end up doing

foo(Object.GetN(), Object.GetM(), Object.GetK())
). A common way to deal with it is to create different overloads for different situations that it might be called:

foo(int n, int m, int k /*, and on and on*/);
foo(bool b, int m/*, ...*/);
foo(int m, int k/*, ...*/);
foo(Object_t object/*, ...*/);
//...


The problem here is that which parameter is which isn't particularly intuitive and you can get quite the surprise when you call a different overload than what you intended.

Recently I had an idea to make it easier to get the function call right and make life easier for myself when dealing with these functions that have many different ways of being called. This solution doesn't cover every possible necessity out there, but it works quite well for me.

Instead of creating different overloads for everything, I would create 1 function that simply takes a variadic number of parameters and then extract possible parameters for use inside the function. As for the parameters, I would wrap them in helper classes that would be created for these functions. This would allow for the user declaring what each integer or boolean or string or what-have-you means instead of relying on positional information within the function's signature.

Instead of
foo(n, m)
(which going by the names of the variables above suggests a likely bug) you would call
foo(OptN(n), OptM(m))
making it completely clear what each parameter is going to be used for and much harder to have a parameter be misinterpreted.

I will include a MCVE at the end if anyone's interested in 1 possible implementation of this.

I have never seen or heard of this technique before, but I also have a hard time believing that I'm the first one to think of it. So, finally, my question is simply does this technique have a name already?

If it doesn't have a name already, I have been calling these functions 'declarative functions' since you declare what each parameter represents explicitly instead of 'positional functions' relying on where the parameter appears to give it its meaning.

MCVE:

#include <iostream>
#include <utility>

struct Option1
{
Option1(bool b):b(b){}
bool b;
bool operator()() const {return b;}
};

struct Option2
{
Option2(int n):n(n){}
int n;
int operator()() const {return n;}
};

struct Group : Option1, Option2
{
Group(bool b, int n):Option1(b), Option2(n){}
};

/*
* Get the option from what the user gave us.
*/
template <class OptionType, class OptionsGetter, class RType>
auto GetOptionImpl(const OptionsGetter & options_getter,
const RType&, std::true_type) ->
decltype(((const OptionType&)options_getter)())
{
return ((const OptionType&)options_getter)();
}

/*
* Get the default value specified since the user didn't pass
* in that option
*/
template <class OptionType, class OptionsGetter, class RType>
RType GetOptionImpl(const OptionsGetter&, const RType & d, std::false_type)
{
return d;
}

/**
* Returns the value of the option OptionType if the user
* passed that in (inside OptionsGetter) and returns the
* default value if they didn't pass it in.
*/
template <class OptionType, class OptionsGetter, class RType>
auto GetOption(const OptionsGetter & oOptionsGetter,
const RType & oDefault) ->
decltype(std::declval<OptionType>()())
{
return GetOptionImpl<OptionType>(oOptionsGetter, oDefault,
std::is_base_of<OptionType, OptionsGetter>());
}

template <class ... Params>
void foo(Params ... params)
{
struct ParamsGetter : Params...
{
ParamsGetter(Params ... p): Params(p)...{}
} params_getter(params...);

if(GetOption<Option1>(params_getter, false))
std::cout << "Option 1 was true ";
else
std::cout << "Option 1 was false ";
std::cout << "Option 2: " << GetOption<Option2>(params_getter, 3) << '\n';
}

int main()
{
foo(Option1{true}, Option2{22});
foo();
foo(Option2{1});
foo(Group(true, 2));
}


Output:

Option 1 was true Option 2: 22
Option 1 was false Option 2: 3
Option 1 was false Option 2: 1
Option 1 was true Option 2: 2

Answer

As mentioned in a comment, this concept is called named parameter. See the explanation on wikipedia, as well as for instance this proposal to introduce it in C++.