Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare two values given pointer and type

Tags:

Say I have a class that stores some data,

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 points to the underlying value and type indicates how the ptr should be cast.

To compare Value objects, I can definitely list all possible type combinations but code will be hard to maintain. like:

if (lhs.type == Type::int_type && rhs.type == Type::float_type) {
    return *static_cast<int*>(lhs.ptr) == *static_cast<float*>(rhs.type);
}

Any idea how to reduce complexity?

Update:

I want this class to be a dynamic type, which means I could do the following:

Value v("abc");       // v is now a string
v = 123;              // v is now an int
bool b = (v == 123.0); // b is true

So I don't think templates are what might be helpful.

like image 754
Einiemand Avatar asked Apr 12 '19 22:04

Einiemand


People also ask

How do you compare the value of a pointer?

You can perform a comparison on pointers with the help of == and != operators provided by the Go language: 1. == operator: This operator return true if both the pointer points to the same variable.

Can you compare two pointer variables?

We can compare pointers if they are pointing to the same array. Relational pointers can be used to compare two pointers. Pointers can't be multiplied or divided.

How can I compare two pointers in C++?

Equality operator (==,!=) From § 5.10 of the C++11 standard: Pointers of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address (3.9. 2).

Can we compare pointer and integer in C?

Comparing a pointer with an integer value in C is generally not meaningful because pointers are not numeric quantities. Most of the cases where it is done are related to systems programming. One use is comparing a pointer with a literal value. In that case, the literal value must be cast to a pointer first.


1 Answers

In effect, what you are trying to do is write a weakly-typed type.

Such types underpin scripting languages such as python and javascript.

Writing a type like this is more complex than you might at first imagine.

However, it becomes easier once you have defined the matrix of conversions (e.g. what are the rules for comparing string to bool?)

Here's a start, using 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);
}
like image 127
Richard Hodges Avatar answered Oct 12 '22 12:10

Richard Hodges