I'm implementing a variant class (not using boost) and I'm wondering how you'd handle the case where you'd store any of string, integer, or double and automatically convert it accordingly to desired type through ToString(), ToInt(), or ToDouble().
For example,
Variant a = 7;
cout << "The answer is" + a.ToString() << endl; // should print "The answer is 7"
a = "7.4";
double &b = a.ToDouble();
b += 1;
cout << a.ToString() << endl; // should print 8.4
ToXXX
functions should return the reference of the type that you want to convert to. Right now, I have the code where it can return the same type as it was initially assigned to( Variant a = Int(7); a.ToInt()
works) and raise exception when the assigned type is different from the one that you want to convert to.
Sorry using boost isn't an option.
Just to let know that C++17 will implement an std::variant to do this.
#include <string>
#include <iostream>
class Variant{
public:
Variant(){
data.type = UNKOWN;
data.intVal = 0;
}
Variant(int v){
data.type = INT;
data.intVal = v;
}
Variant(double v){
data.type = DOUBLE;
data.realVal = v;
}
Variant(std::string v){
data.type = STRING;
data.strVal = new std::string(v);
}
//the missing copy constructor
Variant(Variant const& other)
{
*this = other;// redirect to the copy assignment
}
~Variant(){
if(STRING == data.type){
delete data.strVal;
}
}
Variant& operator = (const Variant& other){
if(this != &other)
{
if(STRING == data.type){
delete data.strVal;
}
switch(other.data.type){
case STRING:{
data.strVal = new std::string(*(other.data.strVal));
data.type = STRING;
break;
}
default:{
memcpy(this, &other, sizeof(Variant));
break;
}
}
}
return *this;
}
Variant& operator = (int newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = INT;
data.intVal= newVal;
return *this;
}
Variant& operator = (double newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = DOUBLE;
data.realVal= newVal;
return *this;
}
Variant& operator = (std::string& newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = STRING;
data.strVal= new std::string(newVal);
return *this;
}
operator int&() {
if(INT == data.type)
{
return data.intVal;
}
//do type conversion if you like
throw std::runtime_error("bad cast");
}
operator double&() {
if(DOUBLE == data.type){
return data.realVal;
}
//do type conversion if you like
throw std::runtime_error("bad cast");
}
operator std::string&() {
if(STRING == data.type){
return *data.strVal;
}
throw std::runtime_error("bad cast");
}
private:
enum Type{
UNKOWN=0,
INT,
DOUBLE,
STRING
};
struct{
Type type;
union
{
int intVal;
double realVal;
std::string* strVal;
};
} data;
};
int main(){
Variant v("this is string");//string
v=1;//int
v=1.0;//double
v=std::string("another string");//
Variant v2; //unkown type
v2=v;//string
std::cout << (std::string&)v2 << std::endl;
return 0;
}
To implement something like this, you need to be able to change the stored type, because if the user changes the variable by reference, he wants the change to affect the value stored, so I'd write something like this:
class Variant
{
private:
enum StoreType
{
Integer,
Float,
String,
}
store_type;
union
{
int * as_integer;
double * as_double;
std::string * as_string;
}
store_pointer;
// convert to type
void integer_to_double();
void integer_to_string();
void double_to_integer();
void double_to_string();
void string_to_integer();
void string_to_double();
public:
...
int & ToInt()
{
switch (store_type)
{
case Integer: break;
case Double: double_to_integer(); break;
case String: string_to_integer(); break;
}
return * as_integer;
}
...
}
I have implemeted a simple variant class myself (without using third-party libs). Each of the ToXxx
functions contains a switch
over m_type
(enum that indicates the type currently being held). For string conversion (both from and to) I use std::stringstream
. It's trivial, really. Pretty much like Mooing Duck suggested.
P. S. If frequent calling of string conversion for the same value is intended, I'd cache it.
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