1

If I manually type this script and call the calculator :

integer array[100];

Then my calculator will create a vector<int> object with the name "array" contains 100 elements.

But C++ code is unique. For example if I type and call :

integer array[100][100];
integer array[100][100][100];
//etc

Then the template vector<int> is illegal.... :(

So annoying! I tried but I could not find the better solution. Could anyone give me some hint?

10
  • What's "my calculator?" Commented Feb 18, 2013 at 11:14
  • It looks like a small script c++ parser, and contains many various mathematical operations. Commented Feb 18, 2013 at 11:15
  • 2
    Nested vectors? I.e. std::vector<std::vector<int>> Commented Feb 18, 2013 at 11:15
  • 2
    You mean, the level of nested arrays should be unlimited in your scripting language. But since scripting = runtime and template instantiation = compile time, you have a problem. Commented Feb 18, 2013 at 11:21
  • 1
    Are you actually generating code, i.e. translating from your special language to C++, and then the C++ program has to be compiled separately? Or are you interpreting your script directly? Commented Feb 18, 2013 at 11:24

2 Answers 2

1

This answer covers to generally different approaches.


To support arbitrarily nested, dynamically sized arrays (so the depth of the nested arrays is not limited during compile time), you should use something like the following.

The type of a variable in your scripting language should be either:

  • integer
  • float
  • (... other primitive types you want to support ...)
  • an array of any of these types (including array)
  • (... other container types such as an associative map if you want to support it ...)

This is typically done using a "variant" type, such as Boost Variant or QVariant in Qt. A variant type is basically a union of a set of types (so it is one of them) plus a type descriptor which tells which type it actually contains.

So an array of any type can be represented, also if this "any type" is an array again. So you can support a dynamic depth of nested arrays.

Note that the "array of any types" should actually be a vector of this variant type. The problem with Boost Variant is that you have to explicitly list the types it can hold as template arguments. This will result in a recursion:

boost::variant<int, float, ..., std::vector<boost::variant<..., ...> > >
                                            ^^^^^^^^^^^^^^^^^^^^^^^^
                                                    recursion

In Qt there is the type QVariant which can hold basically any type supported by Qt. QVariant is not a template class and thus its type doesn't contain such a recursion. I don't know if there is a comparable boost type, but I doubt it.


If your arrays can't be resized during execution of the script (or if they should be resized, you can allocate a new one and copy the contents), there is a simpler solution. Just store the arrays in a one-dimensional vector, also store the dimensions of the array in your scripting language in another vector. Then you can use an index method like the following.

class ScriptArray {
    vector<int> dim;
    vector<int> elements;

    int getIndex(vector<int> indexList) const {
        int multiplicator = 1;
        int index = 0;
        for (int i = 0; i < dim.size(); ++i) {
            index = multiplicator * indexList[i];
            multiplicator *= dim[i];
        }
        return index;
    }
};

This is basically a generalization of the following idea. Consider a two-dimensional array (100 x 100 elements) you want to represent as a one-dimensional one (10000 elements). For any index in the original array (x, y) you can map this to a one-dimensional index for your internal array (x + 100 * y). For a three-dimensional array this just contains another multiplication (x + 100 * y + 100*100 * z) and so on...

The problem with this solution and resizing the array is that the elements "move" within the array when the size of a dimension changes (special case: last dimension, as this dimension is the "outermost" one). So either you can live with the fact that the array would be invalid when resized, or you copy the contents in a new array with the new size, or you implement some complicated resize method which carefully inserts spaces / removes some elements in the array at the correct places.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your trick! But if I have a pointer, such as integer array[100][100] and integer ** pint = array then crazily access an element pint[15][65], can this tweak be applied? And the pointer doesn't contain any information except its address value.
Do you mean you have such a pointer in C++ or in your scripting language? If the user allocates an array in your script, you should store the additional information in C++. So for example pint[15][65] (script) can be evaluated as variable->getElement(indexList) in C++ (where indexList is a vector with 15, 65). The getElement uses getIndex which converts this to a 1D-index, let's call it i, so you can return elements[i] in C++.
0

I am assuming that you have created your own language that you want to parse using C++. Your parser knows that you are defining a variable, since it finds the type integer before an identifier. What you then have to do is check if you have to create a normal variable or a multi-dimensional array.

  • If no brackets ([]) exist -> Normal variable
  • If brackets exist -> Make a loop to count how many there are. Then allocate nested vectors. Only the final vector will have elements of type int.

So the easiest way to implement this is to forget about an array and just view it as a special case of a multi-dimensional array with only one bracket.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.