Developer Skill Sprint: Effective IfDef Usage for Cross Platform Development

Posted by on in Programming

Learn about how to use conditional compilation and platform services to accurately target your code for cross platform development.

Platforms and Delphi Compilers

  • Windows
    • 32-bit DCC32
    • 64-bit DCC64
  • OS X
    • 32-bit DCCOSX
  • iOS
    • 32-bit DCCIOSARM
    • 64-bit DCCIOSARM64
    • 32-bit simulator DCCIOS32
  • Android
    • ARM DCCAARM
      • Works on x86 via libHoudini
      • (Not an officially supported platform)

Platforms and C++ Compilers

  • 32-bit Windows
    • BCC32C.EXE (Clang)
    • BCC32.EXE
  • 64-bit Windows
    • BCC64.EXE (Clang)
  • OS X
    • BCCOSX.EXE
  • 32-bit iOS
    • BCCIOSARM.EXE (Clang)
  • 64-bit iOS
    • BCCIOSARM64.EXE (Clang)
  • Android
    • BCCAARM.EXE (Clang)
      • (also x86 Android via libHoudini)
  • More information http://embt.co/cpp-compilers 

 

FireMonkey Platform Services

  • Discover and access functionality that may or may not be available on current run-time platform.
  • Discover
    • TPlatformServices.Current.SupportsPlatformService
  • Access
    • TPlatformServices.Current.GetPlatformService
  • Can also replace built-in services with your own implementation.
  • More information: http://embt.co/platform-services

Conditional Compilation

  • Use of IFDEF, IF, etc. compiler directives to optionally compile selected code branches
  • Check Predefined Conditionals for compiler features
  • Completely removes branches of code
  • Useful when linking against platform API calls
  • Overuse introduces complexity to code
  • For Delphi "{$IF Defined(conditional)}" = "{$IFDEF conditional}"
  • More information: http://embt.co/conditional-compilation 
  • For C++ "#if defined macro" = "#ifdef macro"
  • More information: http://embt.co/conditional-compilation-cpp 

Predefined Conditionals & Macros

FMX & RTL Examples

  • FMX.Memo.pas
    • Implements interfaces, base classes and common behaviors
    • Only uses uses IFDEFs in implementation Uses statement for platform implementations
      • FMX.Memo.iOS
      • FMX.Memo.Win
  • FMX.WebBrowser.pas
    • Uses compiler directives in Uses
    • Calls RegisterWebBrowserService in platform specific implementations
  • System.Notification.pas
    • Uses compiler directives in Uses
    • Platform specific implementations implement TPlatformNotificationCenter based on TBaseNotificationCenter
    • TBaseNotificationCenter instantiates TPlatformNotificationCenter internally

Best Usage Strategies 

  • Use Platform services when possible
  • Keep conditional defines in “library” code
  • Have project use high level units
    • Defines interfaces, base classes and common code
    • Unit uses clause has conditional defines to platform specific implementation units
  • Keep platform specific code in its own unit
    • e.g. Don’t put iOS and Android code in same unit
  • Create unit with useful constants

Delphi Code Samples

Conditional compilation samples

uses
  FMX.Types;

{ TIfDefTest }

procedure TIfDefTest.IfDefTest;
begin
  {$IFDEF MSWINDOWS}
  Assert.Pass('On Windows');
  {$ELSE}  // optional
  Assert.Fail('Not windows');
  {$ENDIF}
end;

procedure TIfDefTest.IfTest1;
begin
  {$IF Defined(MSWindows) and (CompilerVersion = 30)}
  Assert.Pass('Seattle for Windows');
  {$ELSE}  // optional
  Assert.Fail('Not Seattle for Windows');
  {$IFEND} // or endif
end;

procedure TIfDefTest.IfTest2;
begin
  {$IF (CompilerVersion >= 30) and (RTLVersion >= 30) and (FireMonkeyVersion >= 23)}
  Assert.Pass('Seattle');
  {$ELSE}  // optional
  Assert.Fail('Not Seattle');
  {$ENDIF} // or ifend
end;

Platform Services Samples

uses FMX.Platform; // Defines IFMXClipboardService

{ TPlatformServicesTest }

procedure TPlatformServicesTest.LongWay;
var
  clipboard: IFMXClipboardService;
begin
  // Just check
  if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService) then
  begin
    // only executed when Clipboard Service is supported
    // Need instance of clipboard service before we can use it
    clipboard := IFMXClipboardService(
      TPlatformServices.Current.GetPlatformService(IFMXClipboardService));
    clipboard.SetClipboard(TestValue);
    Assert.AreEqual(clipboard.GetClipboard.AsString, TestValue);
  end
  else
    Assert.Fail('Clipboard service not supported.');
end;

procedure TPlatformServicesTest.ShortWay;
var
  clipboard: IFMXClipboardService;
begin
  // Just check
  if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, clipboard) then
  begin
    // only executed when Clipboard Service is supported
    // Already has instance to Clipboard service
    clipboard.SetClipboard(TestValue);
    Assert.AreEqual(clipboard.GetClipboard.AsString, TestValue);
  end
  else
    Assert.Fail('Clipboard service not supported.');
end;

The code samples were built with DUnitX and ran with TestInsight - free add-in by Stefan Glienke.

C++Builder Code Samples

Conditional Compilation samples built with DUnitX.

void __fastcall TestConditionals::WindowsTest()
{
	#if (_Windows)
	Dunitx::Testframework::Assert::Pass("This is Windows.");
	#else
	Dunitx::Testframework::Assert::Fail("NOT Windows.");
	#endif
}

void __fastcall TestConditionals::SeattleTest()
{
	#if (__CODEGEARC__ >= 0x0710)
	Dunitx::Testframework::Assert::Pass("This is 10 Seattle.");
	#elif (__CODEGEARC__ >= 0x0700)
	Dunitx::Testframework::Assert::Pass("This is XE8.");
	#else
	Dunitx::Testframework::Assert::Fail("NOT Seattle");
	#endif
}

void __fastcall TestConditionals::Win32Test()
{
	#if (__WIN32__)
	Dunitx::Testframework::Assert::Pass("This is Win32.");
	#else
	Dunitx::Testframework::Assert::Fail("NOT Win32.");
	#endif
}

void __fastcall TestConditionals::NotTest()
{
	#if (!__ANDROID__)
	Dunitx::Testframework::Assert::Pass("This is not Android.");
	#else
	Dunitx::Testframework::Assert::Fail("This is ANDROID!");
	#endif
}

void __fastcall TestConditionals::TestiOS()
{
	#if (__APPLE__ && (__arm__ || __arm64__))
	Dunitx::Testframework::Assert::Fail("This would be iOS.");
	#else
	Dunitx::Testframework::Assert::Pass("This is not iOS");
	#endif
}

Conditional compilation sample written with DUnitX

String TestValue = "{BD1A7EB6-C8F6-4561-94AF-3B6F93594F3F}";

#include <FMX.Platform.hpp>

void __fastcall TestPlatformServices::LongWayTest()
{
	if (TPlatformServices::Current->SupportsPlatformService
			(__uuidof(IFMXClipboardService))) {
		// Only executed if platform supports specified service

		// Need instance of service
		_di_IFMXClipboardService ClipboardService =
		  (_di_IFMXClipboardService)TPlatformServices::Current->GetPlatformService
			(__uuidof(IFMXClipboardService));

		ClipboardService->SetClipboard(TValue::From<String>(TestValue));
		Dunitx::Testframework::Assert::AreEqual(TestValue,
		  ClipboardService->GetClipboard().AsString());

	}
	else
	{
		Dunitx::Testframework::Assert::Fail("Service not supported.");
	}
}

void __fastcall TestPlatformServices::ShortWayTest()
{
	_di_IFMXClipboardService ClipboardService;

	if (TPlatformServices::Current->SupportsPlatformService
	  (__uuidof(IFMXClipboardService), &ClipboardService)) {
		// Only executed if platform supports specified service
		// already have instance to service

		ClipboardService->SetClipboard(TValue::From<String>(TestValue));
		Dunitx::Testframework::Assert::AreEqual(TestValue,
		  ClipboardService->GetClipboard().AsString());
	}
	else
	{
		Dunitx::Testframework::Assert::Fail("Service not supported.");
	}
}

Delphi Learning Resources

C++Builder Learning Resources


Delphi Replay
Video not found or Youtube service not available
 
C++Builder Replay
Video not found or Youtube service not available

 



About
Gold User, Rank: 15, Points: 256
This latest season of Developer Skill Sprints focuses on programming tips and techniques most requested by attendees of the past 72 skill sprints we’ve already presented. This quarter we’ll take your

Comments

Check out more tips and tricks in this development video: