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 compatibility✅✅Same 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.