How to have a C++ stack with more than one data type?

c++, stack, string, variant, vector

Solution

Sure, one way is to use a tagged union:

enum Type { INTEGER, DOUBLE, /* ... */ };

union Data {
    uint64_t as_integer;
    double as_double;
    // ...
};

struct Value {
    Type type;
    Data data;
};

The storage for `as_integer`, `as_double`, etc. will be overlapped, so a `Value` structure will take up two words of storage, and your stack will have type `std::vector<Value>`. Then you access members of `data` according to the value of `type`:

void sub(std::vector<Value>& stack) {
    // In reality you would probably factor this pattern into a function.
    auto b = stack.back();
    stack.pop_back();
    assert(b.type == INTEGER);

    auto a = stack.back();
    stack.pop_back();
    assert(a.type == INTEGER);

    Value result;
    result.type = INTEGER;
    result.data.as_integer = a.data.as_integer - b.data.as_integer;
    stack.push_back(result);
}

Of course, Forths are usually untyped, meaning that the stack consists of words only (`std::vector<uint64_t>`) and the interpretation of a data value is up to the word operating on it. In that case, you would pun via a union or `reinterpret_cast` to the appropriate type in the implementation of each word:

void subDouble(std::vector<Data>& stack) {
    // Note that this has no type safety guarantees anymore.
    double b = stack.back().as_double;
    stack.pop_back();

    double a = stack.back().as_double;
    stack.pop_back();

    Data result;
    result.as_double = a - b;
    stack.push_back(result);
}

void subDouble(std::vector<uint64_t>& stack) {
    double b = reinterpret_cast<double&>(stack.back());
    stack.pop_back();

    double a = reinterpret_cast<double&>(stack.back());
    stack.pop_back();

    double result = a - b;
    stack.push_back(reinterpret_cast<uint64_t&>(result));
}

Alternatively, you can store not values but pointers to instances of a class `Value` from which other value types such as `Integer` or `Double` would derive:

struct Value {};
struct Integer : Value { uint64_t value; };
struct Double : Value { double value; };
// ...

Your stack would have type `std::vector<unique_ptr<Value>>` or `std::vector<Value*>`. Then you needn’t worry about different value sizes, at the cost of making wrapper structures and allocating instances of them at runtime.

Problem

Here's the problem: I am currently trying to create a simple stack-based programming language (Reverse Polish Notation, FORTH style) as a component of a larger project. I have hit a snag, though. There is no problem with creating a stack in C++ (by using `std::vector<>`) that would contain one type of element (I could use the syntax `std::vector<double> Stack`, for instance). However, a programming language needs to be able to hold multiple data types, such as ints, doubles, strings, and 3D vectors (as in physics vectors with X, Y, and Z components), just to name some simple things. So, is there a construct in C++ that I could use as a stack that would be able to store more than one kind of primitive type/object/struct?

Original source

Related problems