Get a pointe/reference to a resource string

Let’s say I have the following code:

resourcestring
  RSTest = 'test';

method Test;
begin
  var Tmp := @RSTest;
end;

This does not compile because it tells me that it cannot infer the type. So I tried giving an obviously bogus type hoping the compiler would give me the one to use:

var Tmp: Integer := @RSTest;

This does give me an error, saying Type mismatch, cannot assign “<method pointer>” to “Integer”

Fair enough, I then tried this:

type
  TRSFunction = method : string;

method Test;
begin
  var Tmp: TRSFunction := @RSTest;
end;

But it still complains, this time saying Valid method reference is expected

So what type should I use here?
I’m trying to declare my own PResStringRec type so that I don’t have to modify hundreds of lines of code throughout my application.

Which platform is that, .NET, Island/Windows, or otherwise?

resourcestring RSTest = 'test'; simply maps to const RSTest: String = 'test'; in Delphi compatibility mode, an as such, it’s a String class instance at runtime. All Elements platforms use UTF16 wide strings, but the internal storage if the string type is “undefined”, so there’s no such thing as having a pointer to the memory represented by the string.

What you will want to do is convert the string to a byte array using an encoding of your choosing, for example using the Encoding.GetBytes method (see http://docs.elementscompiler.com/API/ElementsRTL/Classes/Encoding).

What’s the goal you are trying to achieve?

This is all targeting .Net core, and I don’t envision targeting anything else.

What I’m trying to do is to mimic a very convenient construct found in Delphi. Basically, when one accesses a resourcestring, the LoadResourceString method is called to extract it from the resources and return a string
In our case, the LoadResourceString method is hooked by the translation engine so that any resourcestring is automatically translated when converted to string.
However, as the current language may change during the life of the application, we often do not want the translation to happen immediately, but rather when the value is to be displayed to the user.
This is why we use the @RSTest construct which gives us a pointer to the structure used internally to get the required parameters for the Win32 LoadString API, that is a pointer to this record:

  TResStringRec = packed record
    // 32bit = 8 bytes
    // 64bit = 16 bytes
    Module: ^HMODULE;
    Identifier: NativeUint;
  end;

So, in thousands of places, we have the @RSTest construct, we even build constants of PResStringRec values like so:

const
  MyEnumResourceStrings : array[TMyEnum] of PResStringRec = (@SFirstValue, @SSecondValue, @SThirdValue);

Once again, this allows deferring the actual conversion to string to the very latest moment, the one where it gets displayed to the user.

Now, I understand that pointers are not welcome in the .Net world, but I’d rather avoid IFDEFing thousands of places, along with loosing the deferred conversion ability. Hence the reason why I’m trying to store the pointer returned by @RSTest so that I can reuse it later on. I don’t really care for the bytes inside the string, I would just use Tmp^ when the actual value is needed.

To implement this, I thought of declaring a record with an implicit operator converting from the value returned by @RSTest, then with overriding the ^ operator but, if I can’t get the pointer type right, this is not going to be feasible.

I hope this clarifies what I’m trying to achieve.

hrmm this is tricky. Resourcestrings currently turn into const, which aren’t loaded in the runtime (or exe for native code) unless used by code. Constants don’t generally have an address at all.

Now if we were to turn them into (readonly) variables, which makes a bit more sense for resource strings, they would have an address, but you would end up with a pointer (due to the @) and thus unsafe code.

would unsafe code work for you? Because this works?

namespace ConsoleApplication1116;

type
  Program = class
  public

    class method Main(args: array of String): Int32;
    begin
      // add your own code here
      writeLn('The magic happens here.');
      writeLn(ABC[0].s);
    end;

  end;
var
  RSTest: String = 'test';

type 
  TResStringRec = packed record
  public
    s: String;
    class operator implicit(aVal: ^String): TResStringRec; unsafe;
    begin
      exit new TResStringRec(s := aVal^);
    end;
  end;
const 
  ABC: array of TResStringRec = [@RSTest];

end.

(And I’m fine with treating resourcestrings as global readonly variables).

Unsafe is definitely the way we are heading anyway, as we have quite a few other pointer based codes.
So yes, the idea of having resourcestrings treated as readonly variables is fine by me.

Consider this fixed for fridays build.

Note that this will still be a “opaque” pointer that doesn’t give you access to the string data in any reasonable direct form.

As long as I can go back to a string reference, all is fine.

2 Likes