简体   繁体   中英

Dynamic dereference of a n-level pointer

Suppose a n-dimensional array that is passed as template argument and should be traversed in order to save it to a file. First of all I want to find out the size of the elements the array consists of. Thereto I try to dereference the pointers until I get the first element at [0][0][0]...[0]. But I already fail at this stage:

/**
 * @brief save a n-dimensional array to file
 *
 * @param arr: the n-level-pointer to the data to be saved
 * @param dimensions: pointer to array where dimensions of <arr> are stored
 * @param n: number of levels / dimensions of <arr>
 */
template <typename T>
void save_array(T arr, unsigned int* dimensions, unsigned int n){ 
        // how to put this in a loop ??       
        auto deref1 = *arr;
        auto deref2 = *deref1;
        auto deref3 = *deref2;
        // do this n times, then derefn is equivalent to arr[0]...[0], 42 should be printed
        std::cout << derefn << std::endl;
        /* further code */
}

/*
 * test call
 */
int main(){
        unsigned int dim[4] = {50, 60, 80, 50}
        uint8_t**** arr = new uint8_t***[50];
        /* further initialization of arr, omitted here */
        arr[0][0][0][0] = 42;
        save_array(arr, dim, 4);
}

When I think of this from a memory perspective I want to perform a n-indirect load of a given address.

I saw a related question that was asked yesterday: Declaring dynamic Multi-Dimensional pointer

This would help me a lot as well. One comment states it is not possible since types of all expressions must be known at compile-time. In my case there's actually known everything, all callers of save_array will have n hardcoded before passing it. So I think it could be just a matter of defining stuff at the right place what I am yet not able to.

I know I am writing C-style code in C++ and there could be options to achieve this with classes etc., but my question is: Is it possible to achieve n-level pointer dereference by an iterative or recursive approach? Thanks!

Why not just use a data structure like tree with multiple child nodes.

Suppose you need to store n dimensional array values, create a node pointing to the first dimension. Say your first dimension length is 5 then you have 5 child nodes and if your 2nd dimension size is 10. Then for each of these 5 node you have 10 child nodes and so on....

Some thing like,

struct node{
    int index;
    int dimension;
    vector<node*> children;
}

It will be easier to traverse through tree and is much cleaner.

First of all: Do you really need a jagged array? Do you want to have some sort of sparse array? Because otherwise, could you not just flatten your n-dimensional structure into a single, long array? That would not just lead to much simpler code, but most likely also be more efficient.

That being said: It can be done for sure. For example, just use a recursive template and rely on overloading to peel off levels of indirection until you get to the bottom:

template <typename T>
void save_array(T* arr, unsigned int* dimensions)
{
    for (unsigned int i = 0U; i < *dimensions; ++i)
        std::cout << ' ' << *arr++;
    std::cout << std::endl;
}

template <typename T>
void save_array(T** arr, unsigned int* dimensions)
{
    for (unsigned int i = 0U; i < *dimensions; ++i)
        save_array(*arr, dimensions + 1);
}

You don't even need to explicitly specify the number of indirections n , since that number is implicitly given by the pointer type.

You can do basically the same trick to allocate/deallocate the array too:

template <typename T>
struct array_builder;

template <typename T>
struct array_builder<T*>
{
    T* allocate(unsigned int* dimensions) const
    {
        return new T[*dimensions];
    }
};

template <typename T>
struct array_builder<T**> : private array_builder<T*>
{
    T** allocate(unsigned int* dimensions) const
    {
        T** array = new T*[*dimensions];

        for (unsigned int i = 0U; i < *dimensions; ++i)
            array[i] = array_builder<T*>::allocate(dimensions + 1);

        return array;
    }
};

Just this way around, you need partial specialization since the approach using overloading only works when the type can be inferred from a parameter. Since functions cannot be partially specialized, you have to wrap it in a class template like that. Usage:

unsigned int dim[4] = { 50, 60, 80, 50 };
auto arr = array_builder<std::uint8_t****>{}.allocate(dim);
arr[0][0][0][0] = 42;
save_array(arr, dim);

Hope I didn't overlook anything; having this many indirections out in the open can get massively confusing real quick, which is why I strongly advise against ever doing this in real code unless absolutely unavoidable. Also this raw usage of new all over the place is anything but great. Ideally, you'd be using, eg, std::unique_ptr . Or, better yet, just nested std::vectors as suggested in the comments…

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM