3

I would like to be able to have the following construct that exists in Delphi to also work in c++

First the Delphi code:

type
  TSlice = record
  private
    function GetData4: UINT32; inline;
    procedure SetData4(Index: 0..15; Value: UINT32); inline;
  public
    Data8: array[0..7] of UINT64;
    property Data4[index: 0..15]: UINT32 read GetData4 write SetData4;
  end;

Now the c++ code that I have and works:

struct TSlice {
    UINT64 Data8[8];

    __device__ __forceinline__ UINT32 * Data4() { return reinterpret_cast<UINT32*>(Data8); }
}

However, I still have to write

for (auto i = 0; i < 3; i++) {
    Slice->Data4()[lane * 4] = SliverPart;
    Slice->Data4()[lane * 4 + 1] = SliverPart;
}

I would really like to drop the () here: Slice->Data2[lane * 4]

Declaring Data4 as UINT32 *const Data4 = (UINT32 *)&Data8;
In the struct does not help, because that gains me an extra 8 bytes, which is not what I want.
I want the translation to be seamless.

How can I do this?

I was thinking of overloading the [] operator, but that would necessitate making Data4 a sub struct and even then I'm not sure it would work if I want to have everything to work inline (i.e. without any runtime overhead).

Using a union works, but the amount of dots that I need to type in the client code is out of this world (classname.unionname.datamember.arrayname[index])

Johan
  • 74,508
  • 24
  • 191
  • 319

2 Answers2

5

If, and only if, you are using C++Builder then you can utilize its __property extension, which is the direct equivalent of Delphi's property, eg:

struct TSlice
{
private:
    UINT32 GetData4(int Index);
    void SetData4(int Index, UINT32 Value);

public:
    UINT64 Data8[8];
    __property UINT32 Data4[int Index] = {read=GetData4, write=SetData4};
};

In other C++ compilers, this is not directly possible, but you can get close by using some helper proxies: eg:

struct TSlice
{
private:
    UINT32 GetData4(int Index);
    void SetData4(int Index, UINT32 Value);

public:
    UINT64 Data8[8];

    TSlice() : Data4(*this) {}

    struct proxy
    {
    private:
        TSlice &m_slice;
        int m_index;

    public:
        proxy(TSlice &slice, int index) : m_slice(slice), m_index(index) {}

        operator UINT32() { return m_slice.GetData4(m_index); }
        proxy& operator=(UINT32 value) { m_slice.SetData4(m_index, value); return *this; }
    };

    struct property
    {
    private:
        TSlice &m_slice;

    public:
        property(TSlice &slice) : m_slice(slice) {}
        proxy operator[](int Index) { return proxy(m_slice, index); }
    };

    property Data4;
};

Or, you could just use an anonymous union:

struct TSlice 
{
    union {
        UINT64 Data8[8];
        UINT32 Data4[16];
    };
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • The anonymous union works for a read/write property and is the cleanest option. It also allows classes passed as `const` to have their `Datax` member accessed. I have not tried the proxy yet, does is allow passing the `TSlice` as a const parameter and accessing `Data4[]`? – Johan Jan 29 '19 at 13:22
  • @Johan in that case, the `property` would need a const `operator[]` to return a read-only `proxy` that doesn't implement `operator=`. Also, `GetData4()` would need to be declared as `const`, too – Remy Lebeau Jan 29 '19 at 16:26
2

There is no such feature in C++.

I was thinking of overloading the [] operator, but that would necessitate making Data4 a sub struct [...]

But I think this is actually the approach you can get closest to the desired result. Maybe a simple std::array already provides all you need?

class WhatEver
{
public:
    std::array<int, 7> property;
};

WhatEver w;
w.property[0];
for(auto& p : w.property) { /* ... */ }

If you need more fine grained access control or std::array (or even std::vector) proves not being able to solve your needs, some appropriate wrapper class might do so:

class Outer
{
public:
    class Inner
    {
    public:
        [...]& operator[](size_t index);
        [...] begin(); // to allow range based for loop
        [...] end();
        // and const versions of
    private:
        friend class Outer; // so that outer can use the internals
        Inner(); // constructors as needed, destructor, ...
        // data container(s) as needed
    };
    Inner property;
};

Usage remains the same, the nested class might use the data types of its inner container and just forward to the the latter's functions in its own ones.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59