I frequently encounter a kind of problem illustrated in the following minimal hypothetical example.
I have a library function like this:
/**
* `shoes` can take values {0, 1, 2}
* `isNew` can take values {false, true}
* `color` can take values {'r', 'g', 'b'}
*/
template <int shoes, bool isNew, char color>
int coreFun (int p1, int p2, int p3) {
return shoes + isNew + int(color) + p1 + p2 + p3; // shoes price.
}
Now, I need to write a function for clients to use:
/**
* Here, `shoes`, `isNew` and `color` will all be strings or string views
* given by clients.
*
* `p1`, `p2`, `p3` are variables to coreFun().
*/
int clientAPI (
auto && shoes, auto && isNew, auto && color,
int p1, int p2, int p3
)
{
int rst = 0; // result.
if (shoes == "snicker") {
if (isNew == "yes") {
if (color == "red") rst = coreFun <0, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <0, true, 'g'> (p1, p2, p3);
else rst = coreFun <0, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = coreFun <0, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <0, false, 'g'> (p1, p2, p3);
else rst = coreFun <0, false, 'b'> (p1, p2, p3);
}
}
else if (shoes == "leather") {
if (isNew == "yes") {
if (color == "red") rst = coreFun <1, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <1, true, 'g'> (p1, p2, p3);
else rst = coreFun <1, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = coreFun <1, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <1, false, 'g'> (p1, p2, p3);
else rst = coreFun <1, true, 'b'> (p1, p2, p3);
}
}
else { // shoes == "other"
if (isNew == "yes") {
if (color == "red") rst = coreFun <2, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <2, true, 'g'> (p1, p2, p3);
else rst = coreFun <2, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = coreFun <2, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = coreFun <2, false, 'g'> (p1, p2, p3);
else rst = coreFun <2, true, 'b'> (p1, p2, p3);
}
}
return rst;
}
The signature of clientAPI()
shall not be changed.
My implementation of clientAPI()
works but is ridiculously long. Luckily in this example I only have 18 combinations. In real life I had encountered as many as 50 such template parameters. Pain.
I intend to simplify the implementation like the following:
int clientAPI (
auto && shoes, auto && isNew, auto && color,
int p1, int p2, int p3
)
{
using namespace std;
auto clientInputs = tuple(move(shoes), move(isNew), move(color));
constexpr auto mapping = tuple(
tuple(pair("sniker", 0), pair("leather", 1), pair("other", 2)),
tuple(pair("no", false), pair("yes", true)),
tuple(pair("red", 'r'), pair("green", 'g'), pair("blue", 'b'))
);
// ===========================================================================
// How to (i) achieve compile time deduction of the 18 instances
// using a some kind of constexpr loop over `clientInputs` and `mapping`,
// and then (ii) execute the correct instance of coreFun()
// corresponding to `clientInputs`?
//
// If the above is impossible, can we achieve the goal using
// std::function and some kind of compile time lookup table ?
// ===========================================================================
}
My question is listed in the comments above. Moreover, could there be some magic function / class that can take coreFun
, clientInputs
and mapping
as parameters and produce the price of the shoes?
Many thanks!!
A more compact solution:
int clientAPI (
std::string_view shoes, std::string_view isNew, std::string_view color,
int p1, int p2, int p3
)
{
auto reduce_shoes = [&]<bool i_, char c_> ()->int {
if (shoes == "snicker") return coreFun<0, i_, c_> (p1, p2, p3);
else if (shoes == "leather") return coreFun<1, i_, c_> (p1, p2, p3);
else if (shoes == "other") return coreFun<2, i_, c_> (p1, p2, p3);
else throw std::runtime_error("No such shoes");
};
auto reduce_shoes_isNew = [&]<char c_>()->int {
if (isNew == "yes") return reduce_shoes.template operator()<true, c_> ();
else if (isNew == "no") return reduce_shoes.template operator()<false, c_> ();
else throw std::runtime_error("Can only be 'yes' or 'no'");
};
if (color == "red") return reduce_shoes_isNew.template operator()<'r'> ();
else if (color == "green") return reduce_shoes_isNew.template operator()<'g'> ();
else if (color == "blue") return reduce_shoes_isNew.template operator()<'b'> ();
else throw std::runtime_error("No such color");
}