如何比较给定指针和类型的两个值

时间:2019-04-12 22:20:21

标签: c++

说我有一个存储一些数据的类,

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);
}

因此,我认为模板可能不会有所帮助。

3 个答案:

答案 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,依此类推。使用这些从零开始的整数,我们可以用连续的从零开始的整数标记所有类型组合,如下所示:

Live DEMO

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>,则这就是您在帖子中写的intfloat的比较。

然后,我想提出以下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& lhsValue& 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

Live DEMO


比较器

总而言之,Variable::operator==的实现如下。 这里的make_comparator在编译时生成结构comparator,将所有类型组合传递给它的模板参数。 comparator还在编译时创建指向函数check<T>的指针的数组。 因此,两个Value的比较将以O(1)复杂度完成:

Live DEMO

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();
    }