6

On firemonkey TBitmap is Fmx.graphics.TBitmap but on VCL it's VCL.graphics.Tbitmap. Their interface are very similar, and i want to create for example this function

function resizeBitmap(const aBitmap: Tbitmap; const w, h: integer);

As the code in resizeBitmap will be exactly the same for Fmx.graphics.TBitmap or VCL.graphics.Tbitmap i would like to make this function available for both VCL app and FMX app (without duplicate it because it's mean i will simply need to copy past the code and replace in uses Fmx.graphics.TBitmap by VCL.graphics.Tbitmap)

is their a way or a conditional define that can help me in this job ?

Abdullah Ilgaz
  • 719
  • 1
  • 17
  • 39
zeus
  • 12,173
  • 9
  • 63
  • 184
  • You could introduce a conditional I suppose and use that in the parameter list of the function. Have your VCL projects define the VCL conditional, and the FMX projects define the FMX conditional. Not going to be very useful though. You can't build a useful cross platform library like that. – David Heffernan Feb 27 '18 at 20:32
  • AFAIK, they don't have a common ancestor except TPersistent, so no, probably not. You could wrap both with classes implementing the same interface (call it IBitmap) and pass that, but for this one single function, that would be too much overhead. For a complete library, it might make sense. – Rudy Velthuis Feb 27 '18 at 20:32
  • You could make `resizeBitmap()` be a class method of a Generic class, then specify either `FMX.Graphics.TBitmap` or `VCL.Graphics.TBitmap` as the Generic type. If you specify just `TBitmap` as the type, the compiler can decide to use `FMX.Graphics.TBitmap` or `VCL.Graphics.TBitmap` based on which unit you have in the `uses` clause, which you can control conditionally with an `{$IFDEF}` or via the project's "Unit Scope Names" list. But no, there is no *predefined* compiler directive that you can `IFDEF` on to know if you are compiling for FMX or VCL, you have to make your own for that purpose. – Remy Lebeau Feb 27 '18 at 20:33
  • @Remy: the generic class would have to jump through a lot of hoops to access the properties and methods of the generic bitmaps (two totally different types -- with many similar or same properties and methods, but we don't have something like duck typing yet). It would be **much** easier to write the same function twice. – Rudy Velthuis Feb 27 '18 at 20:35
  • @RudyVelthuis: the OP said: "*the code in `resizeBitmap` will be **exactly the same** for `Fmx.graphics.TBitmap` or `VCL.graphics.Tbitmap`*" - that is what Generics is good for. – Remy Lebeau Feb 27 '18 at 20:37
  • maybe this can help: https://scotthollows.com/2016/10/13/delphi-conditional-compile-vcl-firemonkey/ – A Lombardo Feb 27 '18 at 20:37
  • a Lombardo this not only help, it's seam it's the solution ! :) thanks everyone ! – zeus Feb 27 '18 at 20:40
  • @Remy: no, that is not what Delphi generics are good for. As I said, these types are totally unrelated, so you only have an untyped generic (or perhaps a Tpersistent or some such) as constraint, so you can only access the properties or methods (IOW, hardly any) of these, i.e. you can't access any TBitmap-specific properties or methods, so the function would be pretty useless. This could work with C++ templates -- basically, these can do duck typing -- but not with Delphi generics. – Rudy Velthuis Feb 27 '18 at 20:41
  • FWIW, why do so many people (here, in blogs, etc.) always write Generics with a capital, as if it is something special? – Rudy Velthuis Feb 27 '18 at 20:44
  • @ALombardo unfortunatly it's not work this : {$IF FMX.Types.FireMonkeyVersion >= 0} :( :( :( – zeus Feb 27 '18 at 20:44
  • @loki: yes, it does. As the article states, it has been tested in multiple Delphi versions from XE2 onward. – Remy Lebeau Feb 27 '18 at 20:46
  • @RemyLebeau : unfortunatly it's work only after you include uses fmx.types :( that a shame because i don't want to include in my vcl project fmx unit, especially this one that uses so many other fmx unit :( – zeus Feb 27 '18 at 20:54
  • 3
    @loki just make two functions and stop over complicating. – David Heffernan Feb 27 '18 at 21:01
  • @davidHeffernan, unfortunatly i have around 109 functions :( duplicating all of them will be very bad :( shame is that both tbitmap work very similar and use the same interface :( – zeus Feb 27 '18 at 21:03
  • Personally, I'd love to see C++-style templates in Delphi. – Andreas Rejbrand Feb 27 '18 at 21:04
  • I don't think the interface is that similar. Anyway, what you might consider is wrapping each bitmap with a class that implements a common interface. But I think you'll soon realise that is hard because the other types that you need will need wrapping too. – David Heffernan Feb 27 '18 at 21:11
  • @AndreasRejbrand: I'd rather not see templates (these can be emulated using .inc files already), but I'd like to have better constraints, allowing duck typing, i.e. allow any T that has property Width: Integer, etc. But even that would not work here, because these properties are Single in FMX. – Rudy Velthuis Feb 27 '18 at 22:04
  • @loki: an include file would probably work (with a few $IFDEFs, perhaps). You could generate two units, one for FMX and one for VCL with one include file and a little bit extra code. You would only have to write the code once. See my answer. – Rudy Velthuis Feb 27 '18 at 22:10
  • @rudy Templates would solve this problem. Certainly my code base would be much improved by templates. All a bit of a moot point because Emba don't have the expertise to do it, even if they wanted to, which they don't. – David Heffernan Feb 27 '18 at 23:00
  • @david: yes, they could solve this problem. But IMO, they can hardly be precompiled, and you'd have to use include files for them (I don't know any C++ implementation that can do without .h files for templates). I'd rather have proper generics with type-safe duck typing. – Rudy Velthuis Feb 27 '18 at 23:23
  • @David: it is certainly not a matter of expertise. Templates are pretty easy to implement, language-wise. If that were something they'd wanted, they had the expertise: they already built a few C++ compilers, right? – Rudy Velthuis Feb 27 '18 at 23:26
  • 1
    @Rudy Why would you need include files? Just because C++ does it that way doesn't mean it's the only way. Remember the history of C++. Originally built on top of the C linker. Generics with rich constraints could give the same flexibility I suppose. The constraint specification language would have to be very rich. I wonder whether the resulting performance would be as good. But this is moot. They laid off all the talented people and have built nothing of note for years. These C++ compilers that they built? You mean clang I suppose. They didn't build it you know. – David Heffernan Feb 27 '18 at 23:35
  • @David: the goal of one of the previous standards was to have a way to do templates without header files. AFAIK, none of the implementers were able to achieve this, so it was abandoned. But if you have a better idea, I'd like to know about it. – Rudy Velthuis Feb 27 '18 at 23:40
  • @Rudy Even the original Turbo C++ compiler was a product that they bought in. Just as they bought in the code that is the basis for the current delphi front end. – David Heffernan Feb 27 '18 at 23:41
  • They built the predecessors of the Clang compilers too. And these did also have templates. Like I said a rich contraint language would be one of the proposals (the only proper one, IMO) for generics for Java, when Java didn't have them yet. It had where clauses that allowed the kind of duck typing I mentioned. Any T that fulfilled some criteria (e.g. having certain methods with signatures) was a valid T. I forgot the name. – Rudy Velthuis Feb 27 '18 at 23:44
  • @rudy Templates don't exist only in C++. Smalltalk has them. D has them. Do all implementations of templates require header files? No. – David Heffernan Feb 27 '18 at 23:44
  • @rudy No, they bought those compilers in. And then developed them, to be fair. Never very standards compliant though. But they had quality technical compiler people back then. They've all left. If you believe that Emba has significant numbers of talented and able compiler developers then you are smoking something. If these people exist, what are they producing? – David Heffernan Feb 27 '18 at 23:47
  • @david: I don't know if they bought the Turbo C++ compiler, but since then they have enhanced it quite a bit, using their own development team. I don't know if they have the expertise right now, but they certainly had it and could have implemented templates for Delphi if they had wanted. But I **know** from personal talks with their people that they wanted generics and what kind. They were not dissimilar to what I described. – Rudy Velthuis Feb 27 '18 at 23:48
  • Templates in Smalltalk? Link? Certainly not compile-time type-safe, because Smalltalk is dynamically typed. So they must be different. I don't know how they work in D. – Rudy Velthuis Feb 27 '18 at 23:49
  • @rudy "I don't know if they bought the Turbo C++ compiler". I do know. Surely you also know that the experienced people have all been laid off. "I don't know how they work in D". It's not hard to find out. Smalltalk isn't a good analogue for the reasons you mention. My dislike of pre-compiled generics is that it makes optimisation so much harder. As you know, performance is always important for me. But it's all moot because Emba don't care about performance, or don't have the expertise to do anything about it, or both. – David Heffernan Feb 27 '18 at 23:58
  • @David: Smalltalk does not need templates or extra generics, as it is dynamically typed. There are Smalltalk templates, but these are HTML with embedded Smalltalk. Not nearly anything like C++ templates. – Rudy Velthuis Feb 28 '18 at 00:00
  • @Rudy I was talking nonsense about Smalltalk. It's not that it has templates per se. It allows generic or algorithmic programming. And just as with C++ templates it is duck typed, albeit at runtime. The point is that it supports the sort of algorithmic programming that C++ templates enables but that common generics implementations don't. – David Heffernan Feb 28 '18 at 00:03
  • I do know that people have ben laid off. I do know that there are still people there and that they seem to be talented. And no, precompiled generics do not make optimization harder. Precompiled generics generate an intermediate tree that can be optimized on instatiation and sometimes even is. Some of the new intrinsics can help here. – Rudy Velthuis Feb 28 '18 at 00:03
  • @Rudy All that talent and yet the output is of such low quality. Have some more kool aid! – David Heffernan Feb 28 '18 at 00:05
  • @david: hence my request for duck typed constraints. Would help a lot. Every method or property listed in the constraint could be called. Back then, they even had a proposal for a special type for constraints, a "specification" that would make specifying them once and for all possible, so you would have `type Bla etc..` where `TSomeSpec = constraint procedure DoThis(P1: Integer; P2: Pointer); property That; end;`. It could also be defined inline, inside the `< >`. – Rudy Velthuis Feb 28 '18 at 00:09
  • Stop patronizing me (kool aid, etc.). Some seem to have talent. That does not prevent mistakes or bugs. – Rudy Velthuis Feb 28 '18 at 00:11
  • @rudy But what would be the point? The implementation would determine what was needed. You may as well let the compiler work out what functionality the type parameters must support. – David Heffernan Feb 28 '18 at 00:14
  • @Rudy It must be the kool aid that makes you such a fan boy, defending the indefensible. I wait to see how you defend recent criticism. For instance the mysterious disappearance of AppAnalytics. Or the continued marketing claims that OSX App Store is supported. – David Heffernan Feb 28 '18 at 00:15
  • @loki Can you contact me on Skype or Slack regarding this? – Dave Nottage Feb 28 '18 at 03:00

5 Answers5

3

Unfortunately there is no conditional define predefined in Delphi to distinguish between FMX and VCL. Fortunately you can have one with little effort. Create a file named UserTools.proj in %APPDATA%\Embarcadero\BDS\19.0 (for Tokyo) and give it the following content:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
       <DCC_Define>FrameWork_$(FrameworkType);$(DCC_Define)</DCC_Define>
    </PropertyGroup>
</Project>

This allows to check the framework in your code like this:

{$IFDEF FrameWork_VCL}
{$IFDEF FrameWork_FMX}
{$IFDEF FrameWork_None}

The drawback is that this file is user specific.

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
1

You could make this an include:

File bitmapcode.inc

// Here, TBitmap is either VCL or FMX, depending on where you include this. 
procedure ResizeBitmap(Bitmap: TBitmap; NewWidth, NewHeight: Integer);
begin
  Bitmap.Width := NewWidth;
  Bitmap.Height := NewHeight
end;

Now, make a unit called VCL.BitmapTools.pas with something like:

unit VCL.BitmapTools;

interface

uses VCL.Graphics {and what else you need} ;

// Here, TBitmap is VCL.Graphics.TBitmap
procedure ResizeBitmap(Bitmap: TBitmap; NewWidth, NewHeight: Integer);

implementation

{$INCLUDE bitmapcode.inc}

end.

And do the same for FMX:

unit FMX.BitmapTools;

interface

uses FMX.Graphics; // etc...

// Here, TBitmap is FMX.Graphics.TBitmap
procedure ResizeBitmap(Bitmap: TBitmap; NewWidth, NewHeight: Integer);

implementation

{$INCLUDE bitmapcode.inc}

end.

So you get two different units, one for VCL and one for FMX, but (almost) no duplication of code.

No generics

Note that using generics is

  • not necessary if you do it this way
  • not possible for a "generic" bitmap

because in code like

SomeClass<T>.ResizeBitmap(Bitmap: T; NewWidth, NewHeight: Integer); 

T does not have any properties or methods at all, and certainly not properties like Width or Height, so any code that used them would simply not compile.

Conditional compilation

Alternatively, you could use conditional compilation:

uses
{$IF declared(FireMonkeyVersion)}
  FMX.Graphics;
{$ELSE}
  VCL.Graphics;
{$IFEND}

But then again, generics would not be required:

procedure ResizeBitmap(Bitmap: TBitmap; NewWidth, NewHeight: Integer);
begin
  Bitmap.Width := NewWidth;
  Bitmap.Height := NewHeight;
end;

Because TBitmap would refer to the TBitmap that was conditionally compiled in. So forget generics. Use one of the ways above.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • Thanks Rudy, this solution must be the solution but unfortunatly like i say to remy the instruction {$IF declared(FireMonkeyVersion)} work ONLY if you include in the UNIT uses fmx.types :( so In a firemonkey app if you don't include fmx.types (in the unit) then it's return always false :( – zeus Feb 28 '18 at 06:45
  • Then use the *.inc* solution to generate **TWO** different units (one for FMX, one for VCL, with different names), without duplication of code. Some kind of "poor man's template". – Rudy Velthuis Feb 28 '18 at 07:54
  • 1
    Well, there is another way: write two simple wrappers for both TBitmaps that both expose an interface, let's call it IBitmap, and pass that IBitmap to your functions and procedures. Interfaces do not care about nor need class hierarchies. So your functions can be called like: `SetBitmapSize(TVCLBitmapWrapper.Create(myVCLBitmap) as IBitmap, 17, 99);` or `SetBitmapSize(TFMXWrapper.Create(myFMXBitmap) as IBitmap, 123, 83);`. Of course you can also do the wrapping once and then keep the wrapper as long as you need it. – Rudy Velthuis Feb 28 '18 at 10:16
1

Another approach would be to define an interface with the characteristics of both TBitmap versions:

type
  IBitmap = interface
  [GUID here]
    function GetWidth: Integer; // or Single
    procedure SetWidth(Value: Integer);
    // etc...
    property Width: Integer read GetWidth write SetWidth;
    // etc...
  end;

And then write two wrappers, one for each kind of Bitmap:

type
  TVCLBitmapWrapper = class(TInterfacedObject, IBitmap)
  private
    FBitmap: VCL.Graphics.TBitmap;
  public
    constructor Create(From: VCL.Graphics.TBitmap);
    function GetWidth: Integer;
    // etc...
  end;

And something similar for the FMX version. Then you could pass these to your functions:

procedure SetBitmapSize(const Bitmap: IBitmap; H, W: Integer);

And call like:

SetBitmapSize(TVCLBitmapWrapper.Create(MyVCLBitmap) as IBitmap, 33, 123);

or

SetBitmapSize(TFMXBitmapWrapper.Create(MyFMXBitmap) as IBitmap, 127, 99);

Of course, if you must pass this to several functions, you first create the wrapper, pass it to these functions and then, if you want, nil it.

Writing wrappers would be overkill for one simple function like SetBitmapSize, but if you have many functions, it might make sense.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
1

I too would advocate using interfaces. You have two classes that are nearly the same. That's one thing interfaces are made for.

Combining interfaces with class helpers you can define your Util-functions to operate on the interface:

function GetBitmapDimensions(ABitmap: IBitmap): string;
begin
    Result := Format('Height: %d, Width: %d', [ABitmap.Height, ABitmap.Width]);
end;

and easyly use this for FMX:

procedure TForm1.Button1Click(Sender: TObject);
begin
    ShowMessage(GetBitmapDimensions(Image1.Bitmap.AsIBitmap));
end;

as well as for VCL:

procedure TForm1.Button1Click(Sender: TObject);
begin
    ShowMessage(GetBitmapDimensions(Image1.Picture.Bitmap.AsIBitmap));
end;

Here is the code. implements is your friend:

unit Mv.Bitmap;

interface

uses
    Classes;

type
    IBitmap = interface
    ['{YourGuid...}']
        procedure LoadFromFile(const Filename: string);
        procedure SaveToFile(const Filename: string);
        procedure LoadFromStream(Stream: TStream);
        procedure SaveToStream(Stream: TStream);
        procedure SetSize(const AWidth, AHeight: Integer);
        //properties
        function GetHeight: Integer;
        function GetWidth: Integer;
        procedure SetHeight(const Value: Integer);
        procedure SetWidth(const Value: Integer);
        property Height: Integer read GetHeight write SetHeight;
        property Width: Integer read GetWidth write SetWidth;
    end;


implementation

end.

With implements you only need to implement the "missing" functions:

unit Mv.FMX.BitmapHelper;

interface

uses
    Mv.Bitmap,
    FMX.Types;

type

    TIFmxBitmapWrapper = class(TInterfacedObject, IBitmap)
    private
        FBitmap: TBitmap;
    protected
        procedure LoadFromFile(const AFilename: string);
        procedure SaveToFile(const AFilename: string);
        function GetHeight: Integer;
        function GetWidth: Integer;
        property Bitmap: TBitmap read FBitmap implements IBitmap;
    public
        constructor Create(ABitmap: TBitmap);
    end;

    TFmxBitmapHelper = class helper for TBitmap
        function AsIBitmap(): IBitmap;
    end;


implementation

{ TIFmxBitmapWrapper }

constructor TIFmxBitmapWrapper.Create(ABitmap: TBitmap);
begin
    FBitmap := ABitmap;
end;

function TIFmxBitmapWrapper.GetHeight: Integer;
begin
    Result := FBitmap.Height;
end;

function TIFmxBitmapWrapper.GetWidth: Integer;
begin
    Result := FBitmap.Width;
end;

procedure TIFmxBitmapWrapper.LoadFromFile(const AFilename: string);
begin
    FBitmap.LoadFromFile(AFilename);
end;

procedure TIFmxBitmapWrapper.SaveToFile(const AFilename: string);
begin
    FBitmap.SaveToFile(AFilename);
end;

{ TBitmapHelper }

function TFmxBitmapHelper.AsIBitmap: IBitmap;
begin
    Result := TIFmxBitmapWrapper.Create(Self);
end;


end.

The compiler differentiates between parameters that are const and ones, that are not, this means some extra work:

unit Mv.VCL.BitmapHelper;

interface

uses
    Mv.Bitmap,
    Vcl.Graphics;

type

    TIVclBitmapWrapper = class(TInterfacedObject, IBitmap)
    private
        FBitmap: TBitmap;
    protected
        // implement only missing functions (const!!)
        procedure SetSize(const AWidth, AHeight: Integer);
        procedure SetHeight(const AValue: Integer);
        procedure SetWidth(const AValue: Integer);
        property Bitmap: TBitmap read FBitmap implements IBitmap;
    public
        constructor Create(ABitmap: TBitmap);
    end;


    TBitmapHelper = class helper for TBitmap
        function AsIBitmap(): IBitmap;
    end;


implementation

{ TIVclBitmapWrapper }

constructor TIVclBitmapWrapper.Create(ABitmap: TBitmap);
begin
    FBitmap := ABitmap;
end;

procedure TIVclBitmapWrapper.SetHeight(const AValue: Integer);
begin
    FBitmap.Height := AValue;
    //alternative: TBitmapCracker(FBitmap).SetHeight(Value);
end;

procedure TIVclBitmapWrapper.SetSize(const AWidth, AHeight: Integer);
begin
    FBitmap.SetSize(AWidth, AHeight);
end;

procedure TIVclBitmapWrapper.SetWidth(const AValue: Integer);
begin
    FBitmap.Width := AValue;
    //alternative: TBitmapCracker(FBitmap).SetWidth(Value);
end;

{ TBitmapHelper }

function TBitmapHelper.AsIBitmap: IBitmap;
begin
    Result := TIVclBitmapWrapper.Create(Self);
end;

end.
yonojoy
  • 5,486
  • 1
  • 31
  • 60
0

You could make resizeBitmap() be a class method of a Generic class, eg:

type
  TBitmapUtility<T> = class
  public
    class procedure resizeBitmap(const aBitmap: T; const w, h: integer);
  end;

class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
begin
  ...
end;

Then you can specify either FMX.Graphics.TBitmap or VCL.Graphics.TBitmap as the Generic type:

var
  bmp: FMX.Graphics.TBitmap;

TBitmapUtility<FMX.Graphics.TBitmap>.resizeBitmap(bmp, ...);

var
  bmp: VCL.Graphics.TBitmap;

TBitmapUtility<VCL.Graphics.TBitmap>.resizeBitmap(...);

If you specify just TBitmap as the type, the compiler can decide to use FMX.Graphics.TBitmap or VCL.Graphics.TBitmap based on which unit you have in the uses clause, which you can control conditionally:

uses
  ...,
  {$IF Declared(FireMonkeyVersion)}
  FMX.Graphics,
  {$ELSE}
  VCL.Graphics,
  {$IFEND}
  ...;

var
  bmp: TBitmap;

TBitmapUtility<TBitmap>.resizeBitmap(bmp, ...);

Or, use the project's "Unit Scope Names" list instead:

uses
  ...,
  Graphics, // <-- specify either 'Vcl' or 'Fmx' in the Unit Scope Names list...
  ...;

var
  bmp: TBitmap;

TBitmapUtility<TBitmap>.resizeBitmap(bmp, ...);

With that said, you do run into a problem - FMX.Graphics.TBitmap and VCL.Graphics.TBitmap do not have a common ancestor beyond TPersistent, so you can't apply a Generic contraint to T so code like this can compile:

class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
begin
  aBitmap.Width := w;
  aBitmap.Height := h;
end;

You will have to resort to using RTTI to solve this, eg:

uses
  ..., System.Rtti;

type
  TBitmapUtility<T: class> = class
  public
    class procedure resizeBitmap(const aBitmap: T; const w, h: integer);
  end;

class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
var
  Ctx: TRttiContext;
  Typ: TRttiType;
begin
  Typ := Ctx.GetType(TypeInfo(T));
  Typ.GetProperty('Width').SetValue(Pointer(aBitmap), w);
  Typ.GetProperty('Height').SetValue(Pointer(aBitmap), h);
end;

Or:

class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
var
  Ctx: TRttiContext;
  Typ: TRttiType;
  Mth: TRttiMethod;
begin
  Typ := Ctx.GetType(TypeInfo(T));

  Mth := Typ.GetMethod('Resize'); // FMX
  if Mth = nil then
    Mth := Typ.GetMethod('SetSize'); // VCL
  // or use an $IF/$IFDEF to decide which method to lookup...

  if Mth <> nil then
    Mth.Invoke(TObject(aBitmap), [w, h])
  else
  begin
    Typ.GetProperty('Width').SetValue(Pointer(aBitmap), w);
    Typ.GetProperty('Height').SetValue(Pointer(aBitmap), h);
  end;
end;

Actually, if you go the {$IF} or "Unit Scope Names" list approach, and let the compiler decide which TBitmap type to use, then you don't actually need the Generic at all, and don't need RTTI when accessing properties/methods that are common to both TBitmap types (even though they don't have a common ancestor):

uses
  ...,
  {$IF Declared(FireMonkeyVersion)}
  FMX.Graphics,
  {$ELSE}
  VCL.Graphics,
  {$ENDIF}
  // or, just 'Graphics' unconditionally...
  ...;

procedure resizeBitmap(const aBitmap: TBitmap; const w, h: integer);

...

procedure resizeBitmap(const aBitmap: TBitmap; const w, h: integer);
begin
  aBitmap.Width := w;
  aBitmap.Height := h;
end;

...

var
  bmp: TBitmap;

resizeBitmap(bmp, ...);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • thanks remy, my preference will go to {$IF FireMonkeyVersion >= 0} but it's not work when i compile in windows :( – zeus Feb 27 '18 at 20:45
  • 2
    Did you actually try to compile something like that? You can not access `aBitmap.Width`, because a generic `T` does not have any properties like `Width`. **This will not work**! – Rudy Velthuis Feb 27 '18 at 20:46
  • 1
    Delphi generics are not templates. **With C++ templates, this would work, but not with Delphi generics.**. Again, that does not work, and I really wonder who upvoted this. Try it before you post it. This can not work. – Rudy Velthuis Feb 27 '18 at 20:48
  • no sorry it's work you simply need to declare it after uses FMX.Types ... that a shame because finally i must include in my vcl app the unit FMX.Types :( – zeus Feb 27 '18 at 20:49
  • @loki: no, you don't need to (nor should you) include `FMX.Types` in a VCL app. [Read the article](https://scotthollows.com/2016/10/13/delphi-conditional-compile-vcl-firemonkey/) again more carefully. If an identifier referenced in an `{$IF}` statement does not exist, the `{$IF}` will simply evaluate to false. This behavior is even described in [Embarcadero's documentation](http://docwiki.embarcadero.com/RADStudio/en/IF_directive_(Delphi)). – Remy Lebeau Feb 27 '18 at 21:04
  • Rudy, Andreas, I updated my answer with a working compilable example. – Remy Lebeau Feb 27 '18 at 21:07
  • You must use RTTI **and** conditional compilation? If you omit generics, that is not necessary. Simply declare the parameter as `TBitmap` instead of as `T` and **forget generics**. As I said, generics are **not** the way to solve this. Much easier **without** them. – Rudy Velthuis Feb 27 '18 at 21:15
  • @RudyVelthuis: there, are you happy now? – Remy Lebeau Feb 27 '18 at 21:20
  • Not happy. I was just concerned that people would actually think generics would make sense here. Now your code is more or less like mine. – Rudy Velthuis Feb 27 '18 at 21:24
  • @RemyLebeau, I was need to go yesterday :( anyway I try again today and i confirm you the instruction {$IF declared(FireMonkeyVersion)} work ONLY if you include in the UNIT uses fmx.types :( because in a firemonkey app if you don't include fmx.types (in the unit) it's return always false :( – zeus Feb 28 '18 at 06:42
  • @loki and that is perfectly fine. You SHOULD NOT include FMX units in a VCL project, only in an FMX project. So there is no problem. Include `FMX.Types` only in an FMX project, then the `{$IF}` will evaluate as true for FMX and false for VCL. – Remy Lebeau Feb 28 '18 at 08:18
  • 1
    @RemyLebeau : no i can't because the purpose is to detect if i m in a fmx project or in a vcl project. – zeus Feb 28 '18 at 08:55
  • 1
    @loki and this is the only way to do that, unless you create your own Conditional Define in the project settings and IFDEF on that instead. – Remy Lebeau Feb 28 '18 at 15:22