Skip to content

64-bit Platform Support

Overview

InstantObjects provides full support for both 32-bit and 64-bit Windows platforms. However, due to compiler differences between 32-bit and 64-bit Delphi, there are important considerations when implementing business classes for 64-bit compatibility.

The most significant difference is in the Retrieve method implementation, which requires a class function reintroduction in each business class for proper type safety in 64-bit applications.

The 64-bit Retrieve Challenge

Background

In 32-bit Delphi, the following code works perfectly:

pascal
var
  Customer: TCustomer;
begin
  Customer := TCustomer.Retrieve('CUST001');
  // Returns TCustomer without explicit cast
end;

However, in 64-bit Delphi, due to changes in the compiler's type inference and class method resolution, this same code requires explicit casting:

pascal
var
  Customer: TCustomer;
begin
  // Without reintroduction, returns TInstantObject
  Customer := TCustomer.Retrieve('CUST001') as TCustomer;  // ← Cast required!
end;

The Solution

To maintain code compatibility across both 32-bit and 64-bit platforms without explicit casting, each business class must reintroduce the Retrieve class function with its own return type.

Implementation

Conditional Compilation

InstantObjects uses the WINLINUX64 compiler directive to conditionally compile the 64-bit specific code:

pascal
{$IF DEFINED(WIN64) OR DEFINED(LINUX64)}
  {$DEFINE WINLINUX64}
{$IFEND}

This directive is defined in Source\InstantDefines.inc.

Class Declaration

Add the reintroduced Retrieve class function to each business class:

pascal
unit Model;

interface

uses
  InstantPersistence;

type
  TCustomer = class(TInstantObject)
  {IOMETADATA stored 'CUSTOMERS';
    Name: String(100);
    Email: String(100);}
    _Name: TInstantString;
    _Email: TInstantString;
  private
    function GetName: string;
    procedure SetName(const Value: string);
    function GetEmail: string;
    procedure SetEmail(const Value: string);
  public
    {$IFDEF WINLINUX64}
    class function Retrieve(const AObjectId: string;
      CreateIfMissing: Boolean = False;
      ARefresh: Boolean = False;
      AConnector: TComponent = nil;
      const AObjectData: TInstantAbstractObjectData = nil): TCustomer; reintroduce; virtual;
    {$ENDIF}
  published
    property Name: string read GetName write SetName;
    property Email: string read GetEmail write SetEmail;
  end;

Important notes:

  • Use {$IFDEF WINLINUX64} to conditionally compile for 64-bit only
  • Use reintroduce keyword to avoid compiler warnings
  • Use virtual to allow further reintroduction in descendant classes
  • Return type is the specific class (TCustomer), not TInstantObject

Class Implementation

Implement the Retrieve class function in the implementation section:

pascal
implementation

{$IFDEF WINLINUX64}
class function TCustomer.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): TCustomer;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as TCustomer;
end;
{$ENDIF}

function TCustomer.GetName: string;
begin
  Result := _Name.Value;
end;

// ... other implementations

Implementation pattern:

  1. Call inherited Retrieve with all parameters
  2. Cast result to the specific class type (as TCustomer)
  3. Return the properly typed result

Complete Example

Here's a complete example with a class hierarchy:

pascal
unit Model;

interface

uses
  System.Classes,
  InstantPersistence,
  InstantTypes;

type
  // Base Contact class
  TContact = class(TInstantObject)
  {IOMETADATA stored 'CONTACTS';
    Name: String(100);
    Email: String(100);}
    _Name: TInstantString;
    _Email: TInstantString;
  protected
    function GetName: string; virtual;
    procedure SetName(const Value: string); virtual;
    function GetEmail: string; virtual;
    procedure SetEmail(const Value: string); virtual;
  public
    {$IFDEF WINLINUX64}
    class function Retrieve(const AObjectId: string;
      CreateIfMissing: Boolean = False;
      ARefresh: Boolean = False;
      AConnector: TComponent = nil;
      const AObjectData: TInstantAbstractObjectData = nil): TContact; reintroduce; virtual;
    {$ENDIF}
  published
    property Name: string read GetName write SetName;
    property Email: string read GetEmail write SetEmail;
  end;

  // Person inherits from Contact
  TPerson = class(TContact)
  {IOMETADATA
    FirstName: String(50);
    LastName: String(50);}
    _FirstName: TInstantString;
    _LastName: TInstantString;
  private
    function GetFirstName: string;
    procedure SetFirstName(const Value: string);
    function GetLastName: string;
    procedure SetLastName(const Value: string);
  public
    {$IFDEF WINLINUX64}
    class function Retrieve(const AObjectId: string;
      CreateIfMissing: Boolean = False;
      ARefresh: Boolean = False;
      AConnector: TComponent = nil;
      const AObjectData: TInstantAbstractObjectData = nil): TPerson; reintroduce; virtual;
    {$ENDIF}
  published
    property FirstName: string read GetFirstName write SetFirstName;
    property LastName: string read GetLastName write SetLastName;
  end;

  // Company inherits from Contact
  TCompany = class(TContact)
  {IOMETADATA
    CompanyName: String(100);
    TaxId: String(20);}
    _CompanyName: TInstantString;
    _TaxId: TInstantString;
  private
    function GetCompanyName: string;
    procedure SetCompanyName(const Value: string);
    function GetTaxId: string;
    procedure SetTaxId(const Value: string);
  public
    {$IFDEF WINLINUX64}
    class function Retrieve(const AObjectId: string;
      CreateIfMissing: Boolean = False;
      ARefresh: Boolean = False;
      AConnector: TComponent = nil;
      const AObjectData: TInstantAbstractObjectData = nil): TCompany; reintroduce; virtual;
    {$ENDIF}
  published
    property CompanyName: string read GetCompanyName write SetCompanyName;
    property TaxId: string read GetTaxId write SetTaxId;
  end;

implementation

{$IFDEF WINLINUX64}
class function TContact.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): TContact;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as TContact;
end;

class function TPerson.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): TPerson;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as TPerson;
end;

class function TCompany.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): TCompany;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as TCompany;
end;
{$ENDIF}

// Property implementations...
function TContact.GetName: string;
begin
  Result := _Name.Value;
end;

procedure TContact.SetName(const Value: string);
begin
  _Name.Value := Value;
end;

function TContact.GetEmail: string;
begin
  Result := _Email.Value;
end;

procedure TContact.SetEmail(const Value: string);
begin
  _Email.Value := Value;
end;

function TPerson.GetFirstName: string;
begin
  Result := _FirstName.Value;
end;

procedure TPerson.SetFirstName(const Value: string);
begin
  _FirstName.Value := Value;
end;

function TPerson.GetLastName: string;
begin
  Result := _LastName.Value;
end;

procedure TPerson.SetLastName(const Value: string);
begin
  _LastName.Value := Value;
end;

function TCompany.GetCompanyName: string;
begin
  Result := _CompanyName.Value;
end;

procedure TCompany.SetCompanyName(const Value: string);
begin
  _CompanyName.Value := Value;
end;

function TCompany.GetTaxId: string;
begin
  Result := _TaxId.Value;
end;

procedure TCompany.SetTaxId(const Value: string);
begin
  _TaxId.Value := Value;
end;

end.

Usage Examples

With the reintroduced Retrieve method, code works identically in both 32-bit and 64-bit:

Retrieving Objects

pascal
var
  Customer: TCustomer;
  Person: TPerson;
  Company: TCompany;
begin
  // Type-safe retrieval - no casting needed!
  Customer := TCustomer.Retrieve('CUST001');
  Person := TPerson.Retrieve('PERS001');
  Company := TCompany.Retrieve('COMP001');

  try
    ShowMessage(Customer.Name);
    ShowMessage(Person.FirstName + ' ' + Person.LastName);
    ShowMessage(Company.CompanyName);
  finally
    Customer.Free;
    Person.Free;
    Company.Free;
  end;
end;

Polymorphic Retrieval

pascal
var
  Contact: TContact;
  ContactId: string;
  ContactClass: TInstantObjectClass;
begin
  ContactId := 'CONT001';
  ContactClass := TContact;  // Could be TPerson or TCompany at runtime

  // Polymorphic retrieval
  Contact := ContactClass.Retrieve(ContactId) as TContact;
  try
    ShowMessage(Contact.Name);

    // Type checking
    if Contact is TPerson then
      ShowMessage('Is a Person')
    else if Contact is TCompany then
      ShowMessage('Is a Company');
  finally
    Contact.Free;
  end;
end;

Create If Missing

pascal
var
  Customer: TCustomer;
begin
  // Retrieve or create
  Customer := TCustomer.Retrieve('CUST999', True);  // CreateIfMissing = True
  try
    if Customer.IsPersistent then
      ShowMessage('Retrieved existing customer')
    else
    begin
      ShowMessage('Created new customer');
      Customer.Name := 'New Customer';
      Customer.Store;
    end;
  finally
    Customer.Free;
  end;
end;

Additional 64-bit Considerations

TLargeIntField for References

In 64-bit applications, InstantObjects uses TLargeIntField for object references instead of TIntegerField. This is controlled by the USE_LARGEINT_FIELD_FOR_REF define in InstantDefines.inc:

pascal
// Recommended for 64-bit applications
{$DEFINE USE_LARGEINT_FIELD_FOR_REF}

This ensures proper handling of large object IDs and reference counts.

Memory Alignment

64-bit applications have different memory alignment requirements. InstantObjects handles this automatically, but be aware when:

  • Working with binary streams
  • Interfacing with external libraries
  • Using low-level memory operations

Pointer Size

Pointer size is 8 bytes in 64-bit vs 4 bytes in 32-bit. This affects:

  • Memory footprint (larger in 64-bit)
  • Cache behavior
  • Overall memory usage

InstantObjects handles this transparently, but large object graphs will consume more memory in 64-bit.

Migration from 32-bit to 64-bit

Step 1: Update Model Classes

Add the reintroduced Retrieve method to all business classes:

pascal
{$IFDEF WINLINUX64}
class function TYourClass.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean = False;
  ARefresh: Boolean = False;
  AConnector: TComponent = nil;
  const AObjectData: TInstantAbstractObjectData = nil): TYourClass; reintroduce; virtual;
{$ENDIF}

Step 2: Implement Retrieve

Add implementation for each class:

pascal
{$IFDEF WINLINUX64}
class function TYourClass.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): TYourClass;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as TYourClass;
end;
{$ENDIF}

Step 3: Test Both Platforms

Compile and test your application in both 32-bit and 64-bit:

  1. 32-bit: Uses original constructor-based Retrieve
  2. 64-bit: Uses new class function Retrieve
  3. Both should work identically from application code perspective

Step 4: Update Build Configurations

Ensure your project has both 32-bit and 64-bit build configurations:

  • Debug 32-bit
  • Debug 64-bit
  • Release 32-bit
  • Release 64-bit

Best Practices

  1. Always reintroduce Retrieve - Every business class should reintroduce it
  2. Use conditional compilation - Wrap with {$IFDEF WINLINUX64}
  3. Mark as virtual - Allow descendant classes to further reintroduce
  4. Keep implementation simple - Just call inherited and cast
  5. Test both platforms - Ensure compatibility
  6. Enable USE_LARGEINT_FIELD_FOR_REF - For proper 64-bit support
  7. Document platform requirements - Note in class comments

Code Generation Tip

For large models with many classes, consider creating a code snippet or template:

Delphi Code Template

Create a Delphi code template (.dci file) for quick insertion:

pascal
{$IFDEF WINLINUX64}
class function T|.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean = False;
  ARefresh: Boolean = False;
  AConnector: TComponent = nil;
  const AObjectData: TInstantAbstractObjectData = nil): T|; reintroduce; virtual;
{$ENDIF}

And for implementation:

pascal
{$IFDEF WINLINUX64}
class function T|.Retrieve(const AObjectId: string;
  CreateIfMissing: Boolean;
  ARefresh: Boolean;
  AConnector: TComponent;
  const AObjectData: TInstantAbstractObjectData): T|;
begin
  Result := inherited Retrieve(AObjectId, CreateIfMissing, ARefresh,
    AConnector, AObjectData) as T|;
end;
{$ENDIF}

Troubleshooting

Problem: "Cannot assign TInstantObject to TCustomer"

Cause: Missing Retrieve reintroduction in 64-bit build

Solution: Add the reintroduced class function as shown above

Problem: "Method 'Retrieve' hides virtual method of base type"

Cause: Missing reintroduce keyword

Solution: Add reintroduce keyword to declaration:

pascal
class function Retrieve(...): TCustomer; reintroduce; virtual;

Problem: Compile error "Undeclared identifier WINLINUX64"

Cause: Missing include of InstantDefines.inc

Solution: Add to unit:

pascal
{$IFDEF LINUX64}
{$I '../../Source/InstantDefines.inc'}
{$ELSE}
{$I '..\..\Source\InstantDefines.inc'}
{$ENDIF}

Problem: Runtime cast error in 64-bit

Cause: Incorrect return type cast

Solution: Ensure cast matches the class name:

pascal
Result := inherited Retrieve(...) as TYourClass;  // Not as TInstantObject!

Problem: Works in 32-bit but not 64-bit

Cause: Conditional compilation not working correctly

Solution:

  1. Check WINLINUX64 is defined for 64-bit builds
  2. Verify InstantDefines.inc is included
  3. Check project conditional defines

Platform Compatibility Matrix

Feature32-bit64-bitNotes
Retrieve without cast✅ (with reintroduction)Requires class function
TLargeIntFieldOptionalRecommendedFor large IDs
Memory usageLowerHigherPointer size difference
Maximum memory2-4 GB128+ GBOS dependent
Database compatibilitySame database structure
Binary streams⚠️Check pointer serialization

See Also

Version History

  • Version 4.2.0 - Added 64-bit Windows platform support
  • Version 4.2.1 - Added USE_LARGEINT_FIELD_FOR_REF define
  • Version 4.2.2 - Fixed 64-bit specific tests
  • Version 4.3.0 - Improved 64-bit compatibility

Released under Mozilla License, Version 2.0.