我经常遇到以下最小假设示例中所示的一种问题。
我有一个这样的库函数:
/**
* `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.
}
现在,我需要编写一个函数供客户端使用:
/**
* 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 = fun <0, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <0, true, 'g'> (p1, p2, p3);
else rst = fun <0, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = fun <0, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <0, false, 'g'> (p1, p2, p3);
else rst = fun <0, false, 'b'> (p1, p2, p3);
}
}
else if (shoes == "leather") {
if (isNew == "yes") {
if (color == "red") rst = fun <1, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <1, true, 'g'> (p1, p2, p3);
else rst = fun <1, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = fun <1, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <1, false, 'g'> (p1, p2, p3);
else rst = fun <1, true, 'b'> (p1, p2, p3);
}
}
else { // shoes == "other"
if (isNew == "yes") {
if (color == "red") rst = fun <2, true, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <2, true, 'g'> (p1, p2, p3);
else rst = fun <2, true, 'b'> (p1, p2, p3);
}
else { // isNew == "no"
if (color == "red") rst = fun <2, false, 'r'> (p1, p2, p3);
else if (color == "green") rst = fun <2, false, 'g'> (p1, p2, p3);
else rst = fun <2, true, 'b'> (p1, p2, p3);
}
}
return rst;
}
clientAPI()
的签名不得更改。
我的
clientAPI()
实现有效,但是太长了。幸运的是,在这个例子中我只有 18 种组合。在现实生活中我遇到过多达 50 个这样的模板参数。疼痛。
我打算简化实现,如下所示:
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 ?
// ===========================================================================
}
我的问题已在上面的评论中列出。另外,是否有一些神奇的函数/类可以将
coreFun
、clientInputs
和 mapping
作为参数并生成鞋子的价格?
非常感谢!!
您可以使用
std::variant
和 std::integral_constant
按类型仅进行一次转换:
std::variant<std::false_type, std::true_type>
to_variant(bool b)
{
switch (b) {
case false: return std::false_type{};
case true: return std::true_type{};
}
}
std::variant<
std::integral_constant<char, 'r'>,
std::integral_constant<char, 'g'>,
std::integral_constant<char, 'b'>,
>
to_variant_color(std::string_view color)
{
if (color == "red") {
return std::integral_constant<char, 'r'>{};
} else if (color == "green") {
return std::integral_constant<char, 'g'>{};
} else if (color == "blue") {
return std::integral_constant<char, 'b'>{};
}
throw std::runtime_error("Invalid color")
}
std::variant<
std::integral_constant<int, 1>,
std::integral_constant<int, 2>,
std::integral_constant<int, 3>,
>
to_variant_shoes(std::string_view shoes)
{
if (shoes == "snicker") {
return std::integral_constant<int, 0>{};
} else if (shoes == "leather") {
return std::integral_constant<int, 1>{};
} else if (shoes == "other") {
return std::integral_constant<int, 2>{};
}
throw std::runtime_error("Invalid shoes")
}
然后,让
std::visit
进行调度:
int clientAPI (
auto && shoes_str, auto && isNew_str, auto && color_str,
int p1, int p2, int p3
)
{
return std::visit(
[&](auto shoes, auto isNew, auto color){
return fun<shoes(), isNew(), color()>(p1, p2, p3)
},
to_variant_shoes(shoes_str),
to_variant(isNewStr == std::string_view("true")),
to_variant_color(color_str)
);
}