how to static cast raw array into struct that only consists of reference to array

103 Views Asked by At

I have a performance-critical piece of code where an operator shall return a wrapped reference to a raw array:

struct A{
   float (&p)[32]; // << this is the only attribute of A; you may change the type of p into float* but iff there is no other possible solution.
   // ... here follow member-functions of A
};
struct B{
   float (*p)[32]; // << this is mandatory
   A& operator[](size_t i){ return static_cast<A>(p[i]); } // << critical line
};

There is no option to replace B with an array of A, because B references data from raw external code. I could circumvent the issue by changing the member functions of A to non-member functions on float[32], but then I'd have to refactor a significant portion of existing code.

Of course, the cast in the critical line cannot be made. But, I want to accept neither the redundancy of creating an array of A, nor the overhead of constructing (and eventually destructing) an instance of A for every call of operator[].

So how can it be done. Please provide functioning code that achieves the purpose with minimum (ideally none at all) overhead.

2

There are 2 best solutions below

1
On BEST ANSWER

A only has a single member of reference type. This is extremely easy to construct/destruct. It should have the exact same overhead as returning a pointer.

Just construct a new A object with every invocation:

struct B{
   float (*p)[32]; // << this is mandatory
   A operator[](unsigned i){ return A{p[i]}; } // << critical line
};

And there will be no overhead versus using non-member functions on float[32]: https://godbolt.org/z/Md6TP7PPP

0
On

The wrapper for A can be retargeted in c++20 but creating an initial A object also always requires initialization. This can be done to a dummy array.

#include <memory>

struct B {
    float(*p)[32]; // << this is mandatory
};

struct A {
    inline static float dummy[32]{};
    float(&p)[32]=dummy; // << this is the only attribute of A; you may change the type of p into float* but iff there is no other possible solution.
    // ... here follow member-functions of A
    A() : p(p) {}  // ctor from B
    A(const B& b, size_t index) : p(b.p[index]) {}  // ctor from B
    A(float(&p)[32]) : p(p) {}  // ctor from array ref
    void reset_an_A_from_B(const B& b, size_t index) {
        std::destroy_at(this);  // this is not needed in this example since A is trivially destructable
        std::construct_at(this, A{ b.p[index] });
    };
};


int main()
{
    float external_arrays[2][32]{};
    B b1{external_arrays};     // contains two arrays of 32 floats

    A a(b1,0);  // construct a from b, first (and only) array
    a.reset_an_A_from_B(b1, 1);    // reset a to refer to second array in b

    // alternately, construct an A initialized by the dummy static array
    A a1;
    a1.reset_an_A_from_B(b1, 0);    // then reset a to refer to first array in a B object

    // To use the reference in A
    float x = a1.p[31];
}

This allows one to change what an A object refers to dynamically in the code as needed.