What I want to do is to cast each "VariantType" (union of different types) argument to it's type before calling some function from a shared library. what I'm doing so far now is down below. it's just 3 different types and 2 arguments and it takes a lot lines. but I want to achieve this with variant number of argument with 7 different types. this has to do something with variadic template( another relevant question: Template tuple - calling a function on each element ). or if you know some better way let me know.
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;
}
You have a messy, and very partial, runtime reflection mechanism. I'm sure you're feeling a lot of pain with this stuff... so the first things to consider would be:
Having said that - you could, in many cases, be satisfied with C++'s own variant type: std::variant
introduced in C++17, together with std::visit
, and a templated visitor (as in this question - but with two templates).
Here's what this would look like:
#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);
}
There is one caveat, though - this won't extract the c_str()
from your strings. If you want to also do that, you can do one of two things:
const char*
in the variant to begin with.x
and y
, you can have a templated transformer function, which does nothing usually, but applies .c_str()
for const string references.but I don't really like the second approach.
I've finally found the simple way to cast each argument with a variadic template (kinda reinvent the wheel 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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.