Reputation: 5387
I'm trying to provide a uniform interface for two similar types, one dealing with double
s and the other with float
s.
class float_type {
float_type() { /* does floaty stuff */ }
float f();
};
class double_type {
double_type() { /* does doubly stuff */ }
double f();
};
I want to write a class that allocates one or the other depending on what the program needs to do.
I'm perfectly fine with the result of float_type::f()
being converted to double. In fact, it happens anyway.
I tried to write it like this:
class union_type {
bool is_double;
char mem[ sizeof(double_type) > sizeof(float_type)
? sizeof(double_type) : sizeof(float_type) ];
public:
float_or_double_value_reader(bool is_double)
: is_double(is_double)
{
if (is_double) new(mem) double_type();
else new(mem) float_type();
}
~float_or_double_value_reader() {
if (is_double) delete static_cast<double_type*>(mem);
else delete static_cast< float_type*>(mem);
}
double f() {
return (is_doubled
? static_cast<double_type*>(mem)->f()
: static_cast< float_type*>(mem)->f()
);
}
};
But I get invalid static_cast from type 'char [128]' to type 'double_type'
.
I know I could add a member pointers to point to what new
returns,
but that would be redundant, since I already know where mem
is located,
so I want to avoid that.
If I use reinterpret_cast
instead, I get free(): invalid pointer:
at runtime when the union_type
is destroyed.
What's the appropriate method of casting here?
Upvotes: 2
Views: 49
Reputation: 58848
reinterpret_cast
should be the appropriate method of casting.
However, you can't simply delete reinterpret_cast<double_type*>(mem)
because that will not only destroy the object, but also free the memory as if it was allocated with new
- which it wasn't.
You can use reinterpret_cast<double_type*>(mem)->~double_type();
to destroy the object without attempting to free the memory.
Of course the above applies to float_type
as well.
Upvotes: 2
Reputation: 6752
You could use a template base class:
#include <iostream>
template < typename T >
class base_decimal
{
public:
base_decimal(T data) : _data(data) {}
virtual ~base_decimal() {}
T f() { return this->_data; }
base_decimal& operator=(T val) { this->_data = val; }
operator T() { return this->_data; }
friend std::ostream& operator<<(std::ostream& os, const base_decimal& bd)
{
os << bd._data;
return os;
}
private:
T _data;
};
class float_type : public base_decimal<float>
{
public:
float_type(float f) : base_decimal<float>(f)
{
// do float stuff
}
};
class double_type : public base_decimal<double>
{
public:
double_type(double d) : base_decimal<double>(d)
{
// do double stuff
}
};
int main(int argc, char* argv[])
{
float_type f = 1.2f;
double_type d = 2.2;
std::cout << "f = " << f << std::endl;
std::cout << "d = " << d << std::endl;
double rd = d;
double rf = f;
std::cout << "rf = " << rf << std::endl;
std::cout << "rd = " << rd << std::endl;
return 0;
}
Upvotes: 0
Reputation: 785
A better option would be to provide casting operator.
I would have provided a implicit double
casting operator to the float class to achieve the same interface
Upvotes: 0