【发布时间】:2022-01-22 18:53:54
【问题描述】:
我正在编写一个 sn-p 代码,它将自动检查 std::variants 中包含的类型,并根据提取的类型从它的子类中调用一个函数。我想出了一个我想做的有点有限的版本:
#include <variant>
#include <optional>
#include <string>
#include <vector>
#include <type_traits>
#include <stdexcept>
#include <tuple>
#include <iostream>
using atom = std::variant<std::string, int, double>;
class callable {
public:
virtual atom eval(std::vector<atom> args) = 0;
};
template <typename T>
struct name {
static const char * get() {
return typeid(T).name();
}
};
template<typename T>
struct is_optional : std::false_type {};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template<int N, typename... Ts> using pack_n =
typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <int N, typename... T>
void check(std::vector<atom> v) {
if constexpr(!is_optional<pack_n<N, T...>>::value)
if(v.size() != N)
throw std::runtime_error("Wrong number of arguments to " + std::string(name<pack_n<N, T...>>::get()) + ", got " + std::to_string(v.size()));
else {
if(!std::holds_alternative<pack_n<N, T...>>(v[N]))
throw std::runtime_error(std::string("Wrong type in argument ") + std::to_string(N) + name<pack_n<N, T...>>::get() + std::string(", got ") + std::to_string(v[N].index()));
if constexpr(N > 0)
check<N-1, T...>(v);
}
else {
if (!std::holds_alternative<typename pack_n<N, T...>::value_type>(v[N]))
throw std::runtime_error(std::string("Wrong type in argument ") + std::to_string(N) + name<pack_n<N, T...>>::get() + std::string(", got ") + std::to_string(v[N].index()));
if constexpr(N > 0)
check<N-1, T...>(v);
}
}
template <typename... V>
class closure : public callable {
public:
virtual atom apply(V...);
atom eval(std::vector<atom> args) override {
check<sizeof...(V) - 1, V...>(args);
return eval_impl(args, std::index_sequence_for<V...>{});
}
private:
template<typename T>
T get(atom a) {
if constexpr(!is_optional<T>::value)
return std::get<T>(a);
else
return std::optional(std::get<T>(a));
}
template<std::size_t... I>
atom eval_impl(std::vector<atom> &args, std::index_sequence<I...>) {
return apply(get<pack_n<I, V...>>(args[I])...);
}
};
class add : public closure<
int, double, double, std::optional<int>
> {
atom apply(int z, double a, double b, std::optional<int> c) {
return a + b + *c;
}
};
int main(void) {
add f{ };
std::vector<atom> v;
v.push_back((int) 3);
v.push_back((double) 5);
v.push_back((double) 5);
v.push_back((int) 3);
std::cout << "ok?" << std::endl;
std::cout << std::get<double>(f.eval(v));
std::cout << "ok?" << std::endl;
}
期望的行为是调用子类 (add::apply) 中的 apply 方法并使用从变体中解压缩的参数,或者如果类型不匹配,代码将引发异常。另外,std::optional 和std::variant 应该被支持,这样apply 函数可以接受可选参数或指定它接受多种参数。
我已经实现了基本骨架和大部分std::optional 部分,但我仍在努力将它们与std::variant 联系在一起。我不确定如何使用 C++ 模板元编程完成以下任务:
- 在
eval_impl中,允许输入向量的长度与参数包不同,以正确允许std::optional参数。 - 由于我无法追踪的原因,代码当前无法编译。
- 如何在参数中支持
std::variants。 - 是否可以将
apply函数的参数用于模板,这样我就不必多次键入相同的内容。 - 是否有可能以更清洁的方式完成这件事
澄清一下,我希望apply 函数的std::variants 允许任何一种类型,例如以下函数:
atom apply(int z, double a, double b, std::optional<int> c, std::variant<int, double> d);
可以在最后一个参数为int 或double 的情况下调用。
【问题讨论】:
-
你在执行
std::visit吗?即使我把你的帖子看了三遍,我也不知道你想达到什么目的。对我来说,这听起来像是一个 XY 问题! -
我没有实现
std::visit。我想编写一些代码来自动检查std::variants 内部的类型,并使用从中取出的值调用函数。我的编程语言需要它,其中atom表示数据类型。当然,callable会有很多实现,所以这样的操作可以使代码更简洁。如果不是这样,我必须每次自己在实现中提取和验证std::variant内容 - 在整个代码库中可能超过 300-400 次。 -
“我想编写一些代码,自动检查 std::variants 内部的类型,并使用从中取出的值调用函数。”那正是std::visit。也许您希望仅存储某些 var 类型的 std::visit 调用,但这可以在提供给 std::visit 的模板化方法中实现...仍然不知道您想要实现什么...抱歉
标签: c++ templates metaprogramming variant