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.
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.
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.
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).
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With