说我有一个存储一些数据的类,
body {
margin: 0;
padding: 0 0;
perspective: 1px;
transform-style: preserve-3d;
height: 100%;
overflow-y: scroll;
overflow-x: hidden;
}
header {
position: sticky;
top: 0px;
height: auto;
width: 100%;
}
class Value {
public:
enum class Type {
int_type,
float_type,
double_type,
bool_type
};
friend bool operator==(const Value& lhs, const Value& rhs) {
// how to make this function clean and concise?
}
private:
void* ptr;
Type type;
};
指向基础值,ptr
指示应如何转换type
。
要比较ptr
对象,我绝对可以列出所有可能的类型组合,但是代码将很难维护。喜欢:
Value
有什么想法可以减少复杂性吗?
更新:
我希望此类为动态类型,这意味着我可以执行以下操作:
if (lhs.type == Type::int_type && rhs.type == Type::float_type) {
return *static_cast<int*>(lhs.ptr) == *static_cast<float*>(rhs.type);
}
因此,我认为模板可能不会有所帮助。
答案 0 :(得分:3)
实际上,您想要做的是编写一个弱类型。
这类类型支持python和javascript等脚本语言。
写这样的类型比您最初想象的要复杂。
但是,一旦您定义了转换矩阵(例如,将字符串与布尔值进行比较的规则是什么?),
这是一个开始,使用std::variant
:
#include <variant>
#include <string>
#include <type_traits>
#include <algorithm>
#include <cassert>
//
// Step 1 - define your conversion rules
// this is not as trivial as you might think
//
template<class To>
struct convert;
template<>
struct convert<int>
{
template<class From>
auto operator()(From const& from) const -> int
{
return int(from);
}
auto operator()(std::string const& from) const -> int
{
return std::atoi(from.c_str());
}
};
template<>
struct convert<double>
{
template<class From>
auto operator()(From const& from) const -> double
{
return double(from);
}
auto operator()(std::string const& from) const -> double
{
return std::atof(from.c_str());
}
};
template<>
struct convert<bool>
{
template<class From>
auto operator()(From const& from) const -> bool
{
return bool(from);
}
auto operator()(std::string from) const -> bool
{
auto lcase = [](auto ch) { return std::tolower(ch); };
std::transform(from.begin(), from.end(), from.begin(), lcase);
if (from == "true" || from == "yes" || std::atoi(from.c_str()))
return true;
else
return false;
}
};
template<>
struct convert<std::string>
{
template<class From>
auto operator()(From const& from) const -> std::string
{
return std::to_string(from);
}
auto operator()(bool const& from) const -> std::string
{
auto result = std::string();
if (from)
result.assign("true");
else
result.assign("false");
return result;
}
auto operator()(std::string const& from) const -> std::string const&
{
return from;
}
};
//
// Step 2 - use a std::variant
//
struct Value
{
explicit Value(int arg): store_(arg) {}
explicit Value(double arg): store_(arg) {}
explicit Value(bool arg): store_(arg) {}
explicit Value(std::string arg): store_(std::move(arg)) {}
explicit Value(const char* arg): store_(std::string(arg)) {}
friend bool operator==(const Value& lhs, const Value& rhs)
{
auto compare = [](auto &&l , auto&& r)
{
using l_type = std::decay_t<decltype(l)>;
auto conv = convert<l_type>();
return l == conv(r);
};
return std::visit(compare, lhs.store_, rhs.store_);
}
private:
using storage_type = std::variant<int, double, bool, std::string>;
private:
storage_type store_;
};
int main()
{
auto vt = Value(true);
auto vst = Value("true");
assert(vt == vst);
}
答案 1 :(得分:1)
方法
首先,让我们考虑所有基本类型的成对组合,它们对应于枚举类Value::Type
的枚举数。
由于未明确初始化Value::Type
的所有枚举数,因此第一个枚举数的值为0
,第二个枚举数的值为1
,依此类推。使用这些从零开始的整数,我们可以用连续的从零开始的整数标记所有类型组合,如下所示:
std::pair<int , int > --> 4*int_type + int_type = 4*0+0 = 0
std::pair<int , float > --> 4*int_type + float_type = 4*0+1 = 1
std::pair<int , double> --> 4*int_type + double_type = 4*0+2 = 2
... ...
std::pair<bool, bool > --> 4*bool_type + bool_type = 4*3+3 = 15
接下来,我们介绍以下静态成员函数模板Value::check
,该模板为每种类型组合提供通用比较:
template<class T>
static bool check(const Value& lhs, const Value& rhs)
{
return *static_cast<typename T::first_type*>(lhs.ptr)
== *static_cast<typename T::second_type*>(rhs.ptr);
}
例如,如果为T = std::pair<int, float>
,则这就是您在帖子中写的int
和float
的比较。
然后,我想提出以下O(1)方法。
在编译时,我们构造以下数组,该数组存储指向函数的指针,arr[i]
是指向check<T>
的指针,其中T
是上面的第i
类型类型组合:
using comp_f = bool(*)(const Value& lhs, const Value& rhs);
comp_f arr[16] = { &check<std::pair<int, int>>, &check<std::pair<int, float>>, ... };
在运行时,给定Value& lhs
和Value& rhs
,我们计算相应的索引并按如下所示调用适当实例化的函数check<T>
。
可以使用O(1)复杂度完成此过程:
std::size_t idx = 4*static_cast<std::size_t>(lhs.type)
+ static_cast<std::size_t>(rhs.type); // 0 ~ 15.
return arr[idx](lhs, rhs);
组合器
现在我们的问题是如何简单地构造所有类型组合。
我已经回答了almost same question这个问题。
在当前情况下,应用此方法,可以通过以下结构Combinations
生成所有可能的组合(也可以使用max66的方法)。
请注意,这里我使用std::index_sequence
和,因此在C ++ 14及以上版本中均可使用。
但是有various way可以在C ++ 11中实现std::index_sequence
:
template<std::size_t I, class Tuple>
using pairing = std::pair<
typename std::tuple_element<I/std::tuple_size<Tuple>::value, Tuple>::type,
typename std::tuple_element<I%std::tuple_size<Tuple>::value, Tuple>::type>;
template <class T, class Is>
struct make_combinations;
template <class Tuple, std::size_t... Is>
struct make_combinations<Tuple, std::index_sequence<Is...>>
{
using pairs = std::tuple<pairing<Is, Tuple>...>;
};
template<class ...Args>
struct Combinations
{
using types_tuple = typename make_combinations
<std::tuple<Args...>,
std::make_index_sequence<(sizeof...(Args))*(sizeof...(Args))>
>::pairs;
};
使用此Combinations
,我们可以将所有类型组合的元组生成为Combinations<int, float, double, bool>::types_tuple
。
比较器
总而言之,Variable::operator==
的实现如下。
这里的make_comparator
在编译时生成结构comparator
,将所有类型组合传递给它的模板参数。
comparator
还在编译时创建指向函数check<T>
的指针的数组。
因此,两个Value
的比较将以O(1)复杂度完成:
template<std::size_t N, class T>
struct comparator {};
template<std::size_t N, class... Combs>
struct comparator<N, std::tuple<Combs...>>
{
using comp_f = bool(*)(const Value& lhs, const Value& rhs);
const comp_f arr[sizeof...(Combs)];
public:
constexpr comparator() : arr{ &check<Combs>... }
{}
bool operator()(const Value& lhs, const Value& rhs) const
{
const std::size_t idx = N*static_cast<std::size_t>(lhs.type)
+ static_cast<std::size_t>(rhs.type);
return arr[idx](lhs, rhs);
}
};
template<class... Ts>
static constexpr auto make_comparator()
{
return comparator<sizeof...(Ts), typename Combinations<Ts...>::types_tuple>();
}
friend bool operator==(const Value& lhs, const Value& rhs)
{
constexpr auto comp = make_comparator<int, float, double, bool>();
return comp(lhs, rhs);
}
答案 2 :(得分:1)
也许您可以简单地编写一个将每个使用的类型的值转换为double的方法(带有单个普通开关的方法),然后在比较运算符中比较两个double? 就是这样:
private:
double ToDouble() const
{
switch (type)
{
case Type::int_type: return *static_cast<int*>(ptr);
case Type::float_type: return *static_cast<float*>(ptr);
case Type::double_type: return *static_cast<double*>(ptr);
case Type::bool_type: return *static_cast<bool*>(ptr) ? 1.0 : 0.0;
}
}
public:
friend bool operator==(const Value& lhs, const Value& rhs)
{
return lhs.ToDouble() == rhs.ToDouble();
}