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:
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:
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:
{$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:
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
reintroducekeyword to avoid compiler warnings - Use
virtualto allow further reintroduction in descendant classes - Return type is the specific class (
TCustomer), notTInstantObject
Class Implementation
Implement the Retrieve class function in the implementation section:
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 implementationsImplementation pattern:
- Call inherited
Retrievewith all parameters - Cast result to the specific class type (
as TCustomer) - Return the properly typed result
Complete Example
Here's a complete example with a class hierarchy:
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
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
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
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:
// 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:
{$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:
{$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:
- 32-bit: Uses original constructor-based Retrieve
- 64-bit: Uses new class function Retrieve
- 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
- Always reintroduce Retrieve - Every business class should reintroduce it
- Use conditional compilation - Wrap with
{$IFDEF WINLINUX64} - Mark as virtual - Allow descendant classes to further reintroduce
- Keep implementation simple - Just call inherited and cast
- Test both platforms - Ensure compatibility
- Enable USE_LARGEINT_FIELD_FOR_REF - For proper 64-bit support
- 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:
{$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:
{$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:
class function Retrieve(...): TCustomer; reintroduce; virtual;Problem: Compile error "Undeclared identifier WINLINUX64"
Cause: Missing include of InstantDefines.inc
Solution: Add to unit:
{$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:
Result := inherited Retrieve(...) as TYourClass; // Not as TInstantObject!Problem: Works in 32-bit but not 64-bit
Cause: Conditional compilation not working correctly
Solution:
- Check
WINLINUX64is defined for 64-bit builds - Verify
InstantDefines.incis included - Check project conditional defines
Platform Compatibility Matrix
| Feature | 32-bit | 64-bit | Notes |
|---|---|---|---|
| Retrieve without cast | ✅ | ✅ (with reintroduction) | Requires class function |
| TLargeIntField | Optional | Recommended | For large IDs |
| Memory usage | Lower | Higher | Pointer size difference |
| Maximum memory | 2-4 GB | 128+ GB | OS dependent |
| Database compatibility | ✅ | ✅ | Same database structure |
| Binary streams | ✅ | ⚠️ | Check pointer serialization |
See Also
- InstantPersistence - Base TInstantObject class
- Creating Objects - Object creation tutorial
- Retrieving Objects - Object retrieval tutorial
- Primer Demo - Working examples with 64-bit support
- Installation Guide - Platform-specific installation
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
