如何使用可变参数模板强制转换每个变体类型

How to cast each variant type with variadic template

本文关键字:转换 类型 何使用 变参 参数      更新时间:2023-10-16

我想做的是在从共享库中调用某个函数之前,将每个"VariantType"(不同类型的联合(参数转换为其类型。 到目前为止,我现在正在做的事情在下面。 它只有 3 种不同的类型和 2 个参数,需要很多行。 但我想用 7 种不同类型的参数的变体数来实现这一点。 这必须与可变参数模板( 另一个相关问题: 模板元组 - 在每个元素上调用一个函数(。或者,如果您知道一些更好的方法,请告诉我。


template<typename... T>
int call(const char* func_name, T... args) {
// this will call func_name(args...) somehow from a dll binary.
// If you need the implementation : https://hastebin.com/ocoyaniruj.cpp
}
int main(int argc, char** argv) {
const char* func_name = "func_name";
VariantType arg1 = "hello world!";
VariantType arg2 = 3.14;
if (arg1.get_type() == VariantType::INT) {
if (arg2.get_type() == VariantType::INT) {
call(func_name, (int)arg1, (int)arg2);
} else if (arg2.get_type() == VariantType::FLOAT){
call(func_name, (int)arg1, (float)arg2);
} else if (arg1.get_type() == VariantType::STRING){
call(func_name, (int)arg1, arg2.c_str());
}
} else if (arg1.get_type() == VariantType::FLOAT){
if (arg2.get_type() == VariantType::INT) {
call(func_name, (float)arg1, (int)arg2);
} else if (arg2.get_type() == VariantType::FLOAT){
call(func_name, (float)arg1, (float)arg2);
} else if (arg1.get_type() == VariantType::STRING){
call(func_name, (float)arg1, arg2.c_str());
}
} else if (arg1.get_type() == VariantType::STRING){
if (arg2.get_type() == VariantType::INT) {
call(func_name, arg1.c_str(), (int)arg2);
} else if (arg2.get_type() == VariantType::FLOAT){
call(func_name, arg1.c_str(), (float)arg2);
} else if (arg1.get_type() == VariantType::STRING){
call(func_name, arg1.c_str(), arg2.c_str());
}
}
return 0;
}

你有一个混乱的、非常片面的运行时反射机制。我相信你对这个东西感到很痛苦......因此,首先要考虑的是:

  • 你真的需要这个吗?如果你能避免它并坚持编译时反射,那会让你的生活更轻松;或
  • C++是您想要使用的语言吗?如果你刚刚开始你的编程项目,并且这种运行时多态性对你来说是至关重要的,也许另一种语言(例如解释语言?(可能更合适。

话虽如此 - 在许多情况下,您可以对C++自己的变体类型感到满意:std::variantC++17 中引入的,以及std::visit,以及模板化的访问者(如在这个问题中 - 但有两个模板(。

这是如下所示的:

#include <variant>
#include <iostream>
#include <string>
int main() {
using variant_type = std::variant<int, float, std::string>;
variant_type v1{"hello world!"};
variant_type v2{3.14f};
std::visit([](auto&& x, auto&& y) { 
// I used this as a stub:
//
//   std::cout << x << " , " << y << 'n';
//
// but you want:
call(func_name, x, y);
}, v1, v2);
}

神霹雳

不过,有一个警告 - 这不会从字符串中提取c_str()。如果您也想这样做,您可以执行以下两项操作之一:

  1. 首先在变体中存储const char*
  2. 你可以有一个模板化的转换器函数,而不是传递xy,它通常不做任何事情,但对常量字符串引用应用.c_str()

但我不太喜欢第二种方法。

我终于找到了用可变参数模板转换每个参数的简单方法(有点重新发明轮子std::visit(。

template<typename... Targs>
void visit(const char* func_name, int visiting_arg, VariantType front, Targs... p_args) {
if (visiting_arg == 0) {
call(func_name, p_args...);
return;
}
if (front.get_type() == VariantType::INT) {
visit(func_name, visiting_arg-1, p_args..., (int)front);
} else if (front.get_type() == VariantType::FLOAT) {
visit(func_name, visiting_arg - 1, p_args..., (float)front);
} else if (front.get_type() == VariantType::STRING) {
visit(func_name, visiting_arg - 1, p_args..., front.c_str());
}
}
int main(int argc, char** argv) {
const char* func_name = "func_name";
int argcount = 3;
VariantType s = "Hello world!";
VariantType f = 3.14;
VariantType i = 42;
visit(func_name, argcount, s, f, i, VariantType("--placeholder--"));
return 0;
}