Hey all

Hey all

I'm making some refactoring in our app. We have a global variable that I wish to wrap in a singleton. The object is declared as

TOurGlobalVar = array[TOurEnum] of TOurRecord;

The problem is, I can't really wrap that easily, as records are returned by value. I need a way to return the records by reference, such that I could write

OurSingletonGetter[AnEnumValue].FieldName := ANewValue;

and also be able to write

OurSingletonGetter[AnEnumValueOnStringForm].FieldName := ANewValue;

This isn't possible, since the property on the new singleton would return a copy of the record, and thus can't be assigned to, until it is bound to a variable, which is exactly what I wish to avoid.

So, my concrete answer is, is it possible to return the TOurRecord type by reference without using pointers?

Comments

  1. Is it possible to return the TOurRecord type by reference without using pointers?

    Not as a return value. Since a record is a value, and Delphi does not have references like C++. If you want a reference you are looking at pointers or var parameters.

    However, you are probably better to stop using a value type publically. Use a reference type. A class or an interface.

    ReplyDelete
  2. Thanks for the quick response :)

    I feared that this was the answer, but I agree that it is a mistake to use value types publically. The problem now is that the record is used all over the place, and that "small change" I was going to make now suddenly became an "awful big change".

    Though the bigger problem is in fact the fact that this is a global variable, and not a singleton.

    Thanks again :)

    ReplyDelete
  3. Kasper Brohus Allerslev Shared state itself isn't so bad. Shared mutable state is bad. Do you really need it to be mutable? How do you test this system then? I suggest you to design your system in a way that you share only immutable data and mutation is encapsulated in single place where it can be controlled easily.

    ReplyDelete
  4. Linas Naginionis This is also exactly what we are trying to move towards. I am currently working on getting a test suite up and running, and I need to remove the global variable as a dependency for unit testing.

    Integration testing is a whole other headache right now.

    ReplyDelete
  5. Kasper Brohus Allerslev you can keep the dependency if at the start of each test you initialise it to a know state.

    ReplyDelete
  6. Jeroen Wiert Pluimers I know, but I'd much rather have the option to depend on an interface, using an alternate constructor.

    ReplyDelete
  7. I do not see much benefit in changing a global variable into a singleton. IMHO singletons are global variables in disguise. Just a namespace trickery. You remove one global dependency, and fall into another. It breaks the Open/Closed SOLID principle (among others).
    Injection of setting values is a better way, especially for unit testing. In unit testing, the "unit" is not just about namespaces, but about re-entrance and really uncoupled code. Singletons follow a poor pattern, and are to be avoided, but for real global values.

    ReplyDelete
  8. A. Bouchez I get what you are saying, but the records contained in the in the global variable are so big and many that I get a stack overflow when copying them...

    ReplyDelete
  9. You can try the following:
    POurRecord = ^TOurRecord;
    TOurRecord = record;
    ...
    function OurSingleton.Item(idx): POurRecord;
    begin
      result :=@myArray[idx];//myArray[]: TOurRecord;
    end;
    ...
    OurSingleton.Instance.Item(idx).FieldName := 'field'; //delphi compiler magic, no pointer aretmetic needed.

    ReplyDelete
  10. Kasper Brohus Allerslev If the record is passed as an explicit "const" parameter to a function, it is passed by reference. But if you lack the "const" keyword, it would be copied on the stack. Ensure you always wrote "const myRec: aRecord" or even "var myRec: aRecord" in your functions to avoid the copy on stack. It is IMHO cleaner than using a pointer (which would be needed only if you need e.g. a local reference).

    ReplyDelete

Post a Comment