-1

In pascal and delphi, arrays have their lengths stored at some offset in memory from the array's pointer. I found that the following code works for me and it gets the length of an array:

type PInt = ^Integer; //pointer to integer.

Function Length(Arr: PInt): Integer;
var
  Ptr: PInt;
Begin
  Ptr := Arr - sizeof(Integer);
  Result := Ptr^ + 1;
End;

Function High(Arr: PInt): Integer;  //equivalent to length - 1.
Begin
  Result := (Arr - sizeof(Integer))^;
End;

I translated the above code into C++ and it thus becomes:

int Length(int* Arr)
{
  int* Ptr = (Arr - sizeof(int));
  return *reinterpret_cast<char*>(Ptr) + 1;
}

int High(int* Arr)
{
    return *(Arr - sizeof(int));
}

Now assuming the above are equivalent to the Pascal/Delphi versions, how can I write a struct to represent a Pascal Array?

In other words, how can I write a struct such that the following is true:

Length(SomeStructPointer) = SomeStructPointer->size

I tried the following:

typedef struct
{
    unsigned size;
    int* IntArray;
} PSArray;

int main()
{
    PSArray ps;
    ps.IntArray = new int[100];
    ps.size = 100;

    std::cout<<Length((int*) &ps); //should print 100 or the size member but it doesn't.

    delete[] ps.IntArray;
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
  • EDIT: Just so that anyone else looking for an answer sees this.. I have actually succeeded in doing this. I use the following: https://pastebin.com/DBFW8QPC and it works on every platform I have available (Linux Mint 17, OSX Sierra, High Sierra, El-Capitan, Yosemite, Windows 7, 8, 10). I am able to allocate arrays on the C++ side and pass them to FPC code and take advantage of FPC's automatic storage. I don't have to free the memory in C++ or pass pointers around with their size separately. – Brandon Feb 28 '18 at 14:27

2 Answers2

7

In Pascal and Delphi, arrays have their lengths stored at some offset in memory from the array's pointer.

This is not so. The entire premise of your question is wrong. The Delphi functions you present do not work in general. They might work for dynamic arrays. But it is certainly not the case that you can pass an pointer to an array and be sure that the length is stored before it.

And in fact the Delphi code in the question does not even work for dynamic arrays. Your pointer arithmetic is all wrong. You read a value 16 bytes to the left rather than 4 bytes. And you fail to check for nil. So it's all a bit of a disaster really.

Moving on to your C++ code, you are reaping the result of this false premise. You've allocated an array. There's no reason to believe that the int to the left of the array holds the length. Your C++ code is also very broken. But there's little point attempting to fix it because it can never be fixed. The functions you define cannot be implemented. It is simply not the case that an array is stored adjacent to a variable containing the length.

What you are looking for in your C++ code is std::vector. That offers first class support for obtaining the length of the container. Do not re-invent the wheel.

If interop is your goal, then you need to use valid interop types. And Delphi managed dynamic arrays do not qualify. Use a pointer to an array, and a separately passed length.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • maybe he can clone dynamic arrays as they are declared/implemented in Borland C++ Builder RTL – Arioch 'The Jan 01 '14 at 10:58
  • @Arioch'The Allocated off which heap? Correct approach is to use proper interop types. – David Heffernan Jan 01 '14 at 10:59
  • Heap mgr only matters if he would use it, IOW if he would free the data or resize it on the receiver's side. But then it is the same for dyn-array or for pointer+size. Or he should pass "free_me" routine along with data, or use some pre-defined 3rd party common heap manager. But again that is the same for both pointers and dyn-arrays – Arioch 'The Jan 01 '14 at 11:29
  • @Arioch So, you think that Delphi arrays are a good choice for interop? – David Heffernan Jan 01 '14 at 11:34
  • No. They are worse just as any "it works somehow" convention is less reliable than an explicitly defined protocol. I just mean that heap manager issues make no difference here. PS. in some specific conditions though, for the sake of performance, direct passing of dynarray might be preferable than double copying Delphi-native dynarray -> language agnostic interop structure -> C++-native std::vector. However the price would be the burden of ensuring that implicit conventions "what is dynarray" are kept on both sides – Arioch 'The Jan 01 '14 at 13:05
  • BTW, are C++ pointers-arrays warrantied to be "packed", to have no sparse memory alignment? – Arioch 'The Jan 01 '14 at 14:54
  • @Arioch'The Yes. For C and C++ it is true that `&x[a]` is the same address as `&(x+a)`. – David Heffernan Jan 01 '14 at 15:02
  • That is not exactly the same - it depends upon definition of pointers math. Is it fixed that `(int)(x+a) == (int)x + (sizeof x*)*a` ? or may it happen that `x + a` would multiply by `sizeof+gap` that would provide CPU-effective memory alignment ? – Arioch 'The Jan 01 '14 at 18:55
  • @Arioch What I stated is accurate. There are no gaps. Read the C or C++ standards if you want to learn about this. – David Heffernan Jan 01 '14 at 18:57
  • What you stated could be accurate with an without gaps :-) Will I need that hardly i'd find and read specs myself. But for curiosity it is enough to ask those who often read it themselves ;-) Actually for me would be somewhat strange that a language aimed at performance like C++ would not provide memory aligned arrays – Arioch 'The Jan 01 '14 at 19:08
  • @Arioch'The The alignment comes from the base element type. So, for instance `struct { int i; char c; } s` has, typically, 3 padding bytes at the **end**. So it has size 8. If ever there is padding it is inside the structures, and never outside. – David Heffernan Jan 01 '14 at 19:11
  • You mean in the case above `sizeof s` would be 8 rather than 5 ? Including a single value, not an item in an array. – Arioch 'The Jan 01 '14 at 19:25
  • 1
    @Arioch'The Yes indeed. That is exactly what I mean. And that is so precisely so that arrays are properly aligned. – David Heffernan Jan 01 '14 at 19:31
  • Hey, My C++ & Pascal has gotten slightly better since. Just thought I'd share how I got it done: https://pastebin.com/DBFW8QPC It works on every platform that I have available and I no longer have to pass around pointers and sizes. I just allocate array or string, pass it to pascal function and FPC can take care of free-ing it and doing whatever it wants with it. – Brandon Feb 28 '18 at 14:29
6

Why? I can see no good reason to do this. Use idiomatic Pascal in Pascal, use idiomatic C++ in C++. Using sizeof like that also ignores padding, and so your results may vary from platform to platform.

If you want a size, store it in the struct. If you want a non-member length function, just write one that works with the way you wrote the struct. Personally, I suggest using std::array if the size won't change and std::vector if it will. If you absolutely need a non-member length function, try this:

template<typename T>
auto length(const T& t) -> decltype(t.size()) {
    return t.size();
}

That will work with both std::array and std::vector.

PS: If you're doing this for "performance reasons", please profile your code and prove that there is a bottleneck before doing something that will become a maintenance hazard.

Mark
  • 3,806
  • 3
  • 21
  • 32
  • It is not for performance reasons. It's because I'm passing pascal arrays to C++ and vice-versa. I did not want to have to add extra parameters to all my functions for the size. I did not want to have to pass the size to every function either. – Brandon Jan 01 '14 at 01:56
  • @CantChooseUsernames - might I suggest a different approach then: use the struct that you wrote that stores the array and size separately. Write an equivalent struct in Pascal. Ignore the fact that Pascal already stores the size, use the explicitly declared member variable only. – Mark Jan 01 '14 at 02:02
  • 1
    @Mark Pascal does not store the size. The Pascal functions in the question are not correct. – David Heffernan Jan 01 '14 at 02:21