c++dynamicstaticinterpreted-language

Creating dynamic data-type in C++


I'm creating an interpreter of a particular language in C++. After creating a parser, implementing scope resolution etc., the only problem I have is implementing dynamically typed variables.

Following some general advice scattered around, I created VarData and VarType structs:

union VarData{
    int IntData;
    char CharData;
    double DoubleData;
};

enum class VarType {
    Int,
    Char,
    Double
};

and a variable struct (it is obviously incomplete):

struct Variable {

VarData data;

VarType type;

template<typename T>
void operator =(T val) {

    std::string name = typeid(T).name();

    if (name == "char") {
        data.CharData = val;
        type = VarType::Char;
    }
    else if (name == "int") {
        data.IntData = val;
        type = VarType::Int;
    }
    else if (name == "double") {
        data.DoubleData = val;
        type = VarType::Double;
    }

}
};

And this actually kind of works, for instance in this sample code, all assigned values are correctly stored:

int main() {

Variable a;

a = '5';  // a.type is now VarType::Char
a = 57;   // a.type is now VarType::Int
a = 8.032;   // a.type is now VarType::Double

}

The problem I have is that if I want to use the Variable struct, I need operator overloads for all common operators (+, -, /, * etc.), each of which needs to cover all possible pairs of types Variable can take. For instance,

Variable operator + (Variable& v1, Variable& v2) {

    if (v1.type == VarType::Char && v2.type == VarType::Char)
        //return Variable of type int
    else if (v1.type == VarType::Double && v2.type == VarType::Int)
        // return Variable of type double
    else if (...)
}

Is there any other (not involving millions of nested if statements) method of doing that?

Sorry if my question is not exactly clear, I will be happy to provide additional explanation.


Solution

  • One way to handle all the different possible type combinations could be to use double dispatch to execute the operation based on the types involved.

    Double dispatch would simplify the determination of what variant of the operation to execute. This means a lot of if less, leaving either to some clever combination of overload override or a dispatch table the duty to mechanically find the suitable operation. However, it would not really be a mastery of the combinatorial explosion.

    Another more effective way would be to apply some systematic type promotion rules. For example if you want to combine in one operation an integer and a float, you'd convert everything to float before performing the operation.

    If you use the interpreter pattern, you could have a template method pattern to manage the type promotion before invoking the suitable operator overload for two values of the same type.

    Unrelated but important: you need to be aware that the typeid() is not a standardized value. So "int", "double", etc... are a nice implementation, but other strings might be used for other compilers. This makes your code non-portable