1

When you declare dynamic array you do it like this for instance

var
  MyArray: array of Byte;

This way compiler knows that it has to finalize that variable (array) when it gets out of scope. But I want to have dynamic array declared as a Pointer like this

var
  MyArray: Pointer;

I know how to initialize (set length) to such array on the fly in runtime. The problem is the compiler has no clue that it needs to finalize it. I want to know if there is a way to tell the compiler to finalize that pointer as array when out of scope. I would tell him that when I would initialize the array for the first time.

I know this is a hack and please I don't want to hear that it is not a correct way. I have a very specific wish (problem) and I want to know if that is possible to achieve. I guess it is not as compiler does it with compiler magic.

EDIT:

I wanted to keep the question simple but as some want to know more I will tell more. I don't think it is relavant to the question but anyway, here it goes.

I want to conserve 4 bytes (or 8 on 64 bit sytem) for each record. It is more of a play thing. My idea was to insert a pointer to dynamic array inside the variable record TSimpleData. These kind of records do not accept variables that need finalization. Perfectly clear why. But I wanted to know if there is a way to register for finalization later. I was 99% sure there is no way but there is no harm asking to be 100% sure. Bellow is the relevant code in question. I want FComplexData to be a part of TSimpleData as a simple pointer.

 TSimpleData = record { do not pack this record; it is compiler-generated }
    case Byte of
      atInteger:   (VInteger: Integer);
      atCardinal:  (VCardinal: Cardinal);
      atBoolean:   (VBoolean: Boolean);
      atObject:    (VObject: TObject);
      atPointer:   (VPointer: Pointer);
      atClass:     (VClass: TClass);
      atWideChar:  (VWideChar: WideChar);
      atChar:      (VChar: AnsiChar);
    {$IFDEF AnyValue_UseLargeNumbers}
      atInt64:     (VInt64: Int64);
      atExtended:  (VExtended: Extended);
    {$ENDIF}   
  end;

  TAnyValue = packed record   private
    FValueType: TValueType;   
  {$IFNDEF AnyValue_NoInterfaces}
    FInterface: IInterface;   
  {$ENDIF}
    FSimpleData: TSimpleData;
    FComplexData: array of Byte;
    ...    
  end;
2
  • You could use a guard object. Unless you can justify why this would be interesting to anybody else, it will be closed as "too localized". Commented Feb 7, 2013 at 19:47
  • Just for the record the only way to do what I want is to hook _FinalizeRecord in System.pas. But needless to say this is an ugly hack. But the only think that works since in all the years noone on Delphi development team thought calling a preset record finalize is a good idea. Shame. Commented Feb 9, 2013 at 19:26

2 Answers 2

2

There is no way to do what you ask. The only way to make the compiler automatically finalize a dynamic array is to declare a dynamic array. You cannot "insert" a dynamic-array variable into your code without actually declaring it.

Once it's declared, you can assign that pointer to it, and it will get cleaned up just like any other dynamic array. You can wrap that behavior into a function, if you want:

procedure CleanUpArray(P: Pointer);
var
  Arr: array of Byte;
begin
  Pointer(Arr) := P;
end;

If you call CleanUpArray(MyArray), the dynamic array stored in your Pointer variable will get cleaned up — have its reference count reduced by 1, and possibly have its memory freed — although the value stored in MyArray will remain unchanged.

That function works because the type-cast disables any reference-counting code inherent in a dynamic-array assignment statement, so it essentially does a bitwise copy. Once the function returns, though, the compiler-inserted cleanup code for the variable runs. It notices that the variable is non-null, so it assumes there's a dynamic array reference in that variable, and the array gets cleaned up.

It's far better to just declare the variable with its real type. It makes life easier for you and the compiler.

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

6 Comments

Thanks Rob. I assumed that much. I know how I am supposed to use dynamic array, but here I just can't. Its more of a play thing to see if I can optimize the memory consumption any further without compromising speed. I can't call finalize, compiler has to do it for me. I have to use a record. Anyway I am ok with 12 bytes in 32 bit mode, but 24 in 64 bother me a little. But I think I already have the optimal solution.
You can call Finalize, but without a type, it won't do anything. And Finalize is compiler magic, so the type comes from the variable, and that means you still need the variable. You can try manually calling System.FinalizeArray (with an array length of 1) and pass it the typeinfo value. I don't know what you're talking about with the byte counts.
@RobKennedy, the question comes from this blog post: "tvalue-and-other-variants-like-implementation-tests-finale".
I can't call anything it has to be automatic. The point of TAnyValue is to be a variant like generic variable. And thus as a record it is automanaged. I have come to best speed / memory compromise at 12 bytes per a record. I am looking for a way to save another 4 bytes by injecting "array of byte" inside the TSimpleData. But as I suspected there is no way to do this.
Yes as LU RD said the question comes from that blog post. I am just checking if there is any more room for improvement. But it is very solid as it is.
|
0

To answer my own question, there is no straightforward way to do this. But as I don't give up easily a solution was made:

http://www.cromis.net/blog/2013/02/tanyvalue-an-attempt-to-make-the-best-variable-data-container/

It basically does what Embarcadero or Borland should have done ages ago.

6 Comments

have you played with System.InitializeArray?
No, not yet. Thought about it, but it very rarely gets called. Are you aiming at doing a constructor also? I did not find a reliable function that gets called when the record is brought to life.
you also have System._InitializeRecord to play with, but you have to "extract" it from the System.pas in order to use it, I'm not really interested at the moment into the variable container, but if you look at the speed difference, a Variant is pretty fast and it comes out of the box.
System._InitializeRecord is what I meant sorry. No problem getting to it, but as I said its not called for the records by default. Variants use 3x the memory of TAnyValue, are slower and most important use a lot of compiler magic I do not like. I had cases where VarArray basically destibilized an important server product by managing to generate a nasty buffer overflow despite all the checks enabled. But sure they are not as bad as I want them to sound :) I did not make TAnyValue just for performance, I also made it cause I like a good challenge.
Sorry have to fix my statement. System._InitializeRecord is called for records containing reference counted variables. Ok then I can make constructor and destructor for each record. Maybe I will make a general solution out of this.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.