I found that sometimes functions have so many parameters. Many of these parameters will be optional, and sometimes a group of these parameters often comes from one other object (so what you end up doing foo(Object.GetN(), Object.GetM(), Object.GetK())). A common way to deal with it is to create various overloads for various situations, which he can name:
foo(int n, int m, int k );
foo(bool b, int m);
foo(int m, int k);
foo(Object_t object);
The problem is that which parameter is not intuitive, and you may get completely unexpected when you cause a different overload than what you intended.
Recently, I had the idea to facilitate the correct call to a function and make my life easier when it comes to these functions, which have many different ways of calling. This solution does not cover all the possible need there, but for me it works well.
Instead of creating different overloads for everything, I would create 1 function that simply takes a variable number of parameters and then extracts the possible parameters for use inside the function. As for the parameters, I would wrap them with helper classes that will be created for these functions. This would allow the user to declare that every integer is either logical or string or something, instead of relying on positional information inside the function signature.
foo(n, m) ( ), foo(OptN(n), OptM(m)), , , .
MCVE , - 1 .
, , , . , , , ?
, " ", , , " ", , , .
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){}
};
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)();
}
template <class OptionType, class OptionsGetter, class RType>
RType GetOptionImpl(const OptionsGetter&, const RType & d, std::false_type)
{
return d;
}
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));
}
:
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