Search This Blog

Tuesday, November 28, 2023

Skia in Delphi 12: Pros and Cons


Skia is a cross-platform 2D graphics library that is now available for Delphi 12. This library is a powerful tool for creating high-quality graphics, and it can be used to develop a wide variety of applications, including games, user interfaces, and more.

In this blog post, we will discuss the pros and cons of using Skia in Delphi 12.

Pros

  • Cross-platform: Skia is a cross-platform library, which means that it can be used to develop applications that run on a variety of platforms, including Windows, macOS, Linux, Android, and iOS.
  • High-performance: Skia is a high-performance library, which means that it can be used to create applications that are smooth and responsive.
  • Hardware-accelerated: Skia can be hardware-accelerated, which means that it can take advantage of the GPU to improve performance.
  • Feature-rich: Skia is a feature-rich library, which means that it provides a wide variety of features for creating graphics.
  • Easy to use: Skia is relatively easy to use, especially for developers who are familiar with Delphi.

Cons

  • New to Delphi: Skia is a new library to Delphi, which means that there is not a lot of documentation or support available yet.
  • Learning curve: Skia has a bit of a learning curve, especially for developers who are not familiar with 2D graphics programming.
  • Not officially supported by Embarcadero: Skia is not officially supported by Embarcadero, which means that there is no guarantee that it will be compatible with future versions of Delphi.

Overall

Skia is a powerful and versatile library that can be used to create high-quality graphics in Delphi 12. However, there are a few things to keep in mind before using this library, such as the fact that it is new to Delphi and not officially supported by Embarcadero.

Here are some additional things to consider when using Skia in Delphi 12:

  • Your target platform: If you are targeting a specific platform, such as Windows or macOS, you may want to use a platform-specific graphics library instead of Skia.
  • Your performance requirements: If you need very high performance, you may want to consider using a hardware-accelerated graphics library, such as OpenGL or Vulkan.
  • Your development experience: If you are not familiar with 2D graphics programming, you may want to consider using a higher-level graphics library, such as FireMonkey or VCL.

Ultimately, the decision of whether or not to use Skia in Delphi 12 depends on your specific needs and requirements.

Skia4Delphi on GitHub: https://github.com/skia4delphi/skia4delphi

Have a look for the demo video on YouTube: https://youtu.be/nZLnGhkp08E?si=0HCMggdepHn9E7S3


Wednesday, August 2, 2023

What Are the Greenest Programming Languages?




There is a recent study that compared the energy consumption of 27 of the most popular languages, using ten different programming problems.

Based on this research the most environmentally friendly languages in terms of least energy usage are C, C++, Rust, and Java.

The lowest-ranking languages are Ruby, Python, and Perl.

What about the Pascal?

As you can see, It is evident that Pascal is performing exceptionally well after 50 years, surpassing Swift, C#, GO, Dart, F#, JavaScript, TypeScript, PHP, Erlang, Ruby, Python, and numerous other programming languages.

The team used Intel’s Running Average Power Limit (RAPL) tool to measure power consumption, which can provide very accurate power consumption estimates.

Abstract of the paper:

This paper presents a study of the runtime, memory usage, and energy consumption of twenty-seven well-known software languages. We monitor the performance of such languages using ten different programming problems, expressed in each of the languages. Our results show interesting findings, such as slower/faster languages consuming less/more energy, and how memory usage influences energy consumption. We show how to use our results to provide software engineers support to decide which language to use when energy efficiency is a concern.”


You can find more info in this regard here on the original report: https://greenlab.di.uminho.pt/wp-content/uploads/2017/10/sleFinal.pdf



Monday, July 31, 2023

EasyDBMigrator


EasyDbMigrator is a database migration library designed for Delphi. It simplifies database evolution and is available in both 32-Bit and 64-Bit versions.

Find it here: https://github.com/AliDehbansiahkarbon/EasyDBMigrator 

Migration:

It uses a concept called Migration, migrations are structured objects designed to alter your database schema. They provide an alternative to creating numerous SQL scripts requiring manual execution by every developer involved.

When dealing with multiple databases, such as the developer's local, test, and production databases, migrations are a helpful solution for evolving a database schema. 
These changes to the schema are recorded in Delphi classes, which can then be committed to a version control system.

Covered databases and available examples:

NameSimpleAdvancedORMLargeScript Execution
Microsoft SQL SERVER
MySQL
MariaDB
PostgreSQL--
Oracle--

Supported Delphi Versions:

Delphi XE5
Delphi XE6
Delphi XE7
Delphi XE8
Delphi 10 Seattle
Delphi 10.1 Berlin
Delphi 10.2 Tokyo
Delphi 10.3 Rio
Delphi 10.4 Sydney
Delphi 11.0 Alexandria
Refer to this link for more information.

Tuesday, July 18, 2023

Demystifying Partial Classes in Delphi

In programming, partial classes offer a powerful tool for code organization and separation. They allow developers to split the definition of a single class across multiple files, making it easier to manage large and complex codebases.

Understanding Partial Classes

In modern programming languages like C#, a partial class is a class that is split into multiple parts (files) but behaves as if it were defined in a single file. 
Each part contains a section of the class's definition, such as properties, methods, or events. 
These files are combined at compile time to create a unified class definition.


Splitting Responsibilities

One of the significant advantages of partial classes is the ability to split the implementation of a class across multiple places(files) based on responsibilities. 
For example, you can have one part dedicated to UI-related code, another for database interactions, and another for business logic. 
This separation helps keep code organized and maintainable.

Collaboration and Teamwork

Partial classes facilitate teamwork by allowing multiple developers to work on different aspects of a class simultaneously. Each developer can focus on a specific section, reducing the chances of merge conflicts and enabling parallel development.

Code Reusability:

Partial classes also promote code reusability. You can share common sections among different classes, allowing multiple classes to access shared code while keeping their own specific implementation separate.

Enhanced Maintainability:

By dividing a class into modular sections(files), partial classes make maintenance and refactoring more manageable. It is easier to locate and modify specific parts of a class without affecting other parts, reducing the risk of introducing bugs inadvertently.

Limitations:

While partial classes offer numerous benefits, it is crucial to be aware of their limitations. 
Modern languages impose certain rules for using partial classes. 
For instance, all parts of a partial class must have the same visibility (private, protected, etc.), and you cannot split properties or fields across files mostly.

Does Delphi support partial classes?

Well, not yet!

We should wait for Embarcadero to implement such a great ability in Delphi.

But!

Actually, there is something in Delphi (coming from the Pascal age) that is called include, 
using the include directive you can break or split your code somehow, it's not the same as a partial class in C# but still useful.

let me show you this ability with a piece of code:

  TMyClass = partial class
    procedure P1(AValue: string);
    procedure P2;
  end;
  
  procedure TMyClass.P1(AValue: string);
  begin
    {$I External.inc}
  end;

  procedure TMyClass.P2;
  begin
    ShowMessage('P2');
  end;  

How does the "TMyClass.P1" method work?
The answer is hidden in another file which is literally a text file and could be with any extension!
In my case, there is one line in this file without respecting the Pascal structure.

  ShowMessage(AValue);

I hope this wonderful feature will be added to Delphi soon.

Thursday, July 13, 2023

ChatGPTWizard! a ChatGPT plug-in for Embarcadero RAD Studio.


 
ChatGPTWizard is an AI plug-in for Embarcadero RAD Studio IDE that supports ChatGPT, Writesonic, and YouChat. It is the first plugin ever to support these services.
You will need a separate API key to use this plugin with each service. 
With ChatGPTWizard, Delphi developers can ask questions directly inside the IDE.
Here are the key features of this plug-in:

Key Features:
  • Free text question form.
  • Dockable question form.
  • Inline questions(in the editor).
  • Context menu options to help you to find bugs, write tests, optimize code, add comments, etc...
  • Class view(Code conversion, code optimizing, test units, and other options per class).
  • Predefined Questions for class view.
  • History to save your tokens on OpenAI !
  • Fuzzy string match searches in the history.
  • Animated letters(Like the website).
  • AI Code Translator
  • Proxy server options.
  • Supports Writesonic AI as the second AI service.
  • Supports YouChat AI as the third AI service.

Remarks

  • It's compatible with Delphi 10.1 Berlin and later versions.
  • Uses XSuperObject library which is included in the project files. you can also find the latest version here
  • Settings are stored in registry which can be found here: Computer\HKEY_CURRENT_USER\Software\ChatGPTWizard

How to Use

Watching a quick demo would be more efficient than hundreds of lines reading I believe.



Other Videos:

Where to find it?

It is available on Getit package manager and GitHub.

Have a look at the repository's readme on GitHub for more info.

TMyObject = object !!😲

Yes sir!

There was an old object in TurPascal that was called `Object`!

In Turbo Pascal, the Object type is a base object type that does not require explicit memory management like classes.
You can create and use objects of this type without the need to explicitly free them. 
Here's a sample code demonstrating the usage of the Object type in Turbo Pascal:


program ObjectDemo;

type
  TMyObject = object
    Value: Integer;
    procedure Initialize(AValue: Integer);
    procedure Display;
  end;

procedure TMyObject.Initialize(AValue: Integer);
begin
  Value := AValue;
end;

procedure TMyObject.Display;
begin
  Writeln('Value:', Value);
end;

var
  MyObject: TMyObject;

begin
  MyObject.Initialize(42);
  MyObject.Display;

  // No need to free the object explicitly

  Readln;
end.



In this code, we define a type `TMyObject` using the `object` keyword. It has a member variable `Value` of type `Integer` and two methods: `Initialize` and `Display`.

The Initialize method sets the value of `Value` based on the provided parameter. The `Display` method prints the value of `Value` to the console.

Inside the `begin` and `end` block, we declare a variable `MyObject` of type `TMyObject`. We then call the `Initialize` method on `MyObject` to set its value to 42 and call the `Display` method to print the value to the console.

Since `TMyObject` is an object, it does not require explicit memory management. The object is automatically allocated and deallocated when it goes out of scope.

Note that the `object` type in Turbo Pascal does not support inheritance or virtual methods like classes do. It is a simpler construct primarily used for encapsulating data and methods within a single unit of code.

Is it available yet in modern Delphi?

Yes, there is, in the matter of backward compatibility.

Implementing Interfaces by Delegation in Delphi!

Implementing Interfaces by Delegation

Is it possible at all?😮

The implements directive allows you to delegate the implementation of an interface to a property in the implementing class. For example:


  property MyInterface: IMyInterface read FMyInterface implements IMyInterface;

Declares a property called MyInterface that implements the interface IMyInterface.

The implements directive must be the last specifier in the property declaration and can list more than one interface, separated by commas. The delegate property:

  • Must be of a class or interface type.
  • Cannot be an array property or have an index specifier.
  • Must have a read specifier. If the property uses a read method, that method must use the default register calling convention and cannot be dynamic (though it can be virtual) or specify the message directive.

The class you use to implement the delegated interface should derive from System.TAggregatedObject.

Delegating to an Interface-Type Property

If the delegate property is of an interface type, that interface, or an interface from which it derives, must occur in the ancestor list of the class where the property is declared. The delegate property must return an object whose class completely implements the interface specified by the implements directive, and which does so without method resolution clauses. For example:

type
  IMyInterface = interface
    procedure P1;
    procedure P2;
  end;
  TMyClass = class(TObject, IMyInterface)
    FMyInterface: IMyInterface;
    property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
  end;
var
  MyClass: TMyClass;
  MyInterface: IMyInterface;
begin
  MyClass := TMyClass.Create;
  MyClass.FMyInterface := ...// some object whose class implements IMyInterface
  MyInterface := MyClass;
  MyInterface.P1;
end;

Delegating to a Class-Type Property

If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched. Thus it is possible to implement some methods in the class specified by the property, and others in the class where the property is declared. Method resolution clauses can be used in the usual way to resolve ambiguities or specify a particular method. An interface cannot be implemented by more than one class-type property. For example:

type
  IMyInterface = interface
    procedure P1;
    procedure P2;
  end;
  TMyImplClass = class
    procedure P1;
    procedure P2;
  end;
  TExternalClass = class(TInterfacedObject, IMyInterface)
  private
    FMyImplClass: TMyImplClass;
  public  
    procedure IMyInterface.P1 = MyP1AlternativeProcedure;
    procedure MyP1AlternativeProcedure;
    
    property MyImplClass: TMyImplClass read FMyImplClass implements IMyInterface;
  end;
  
  procedure TMyImplClass.P1;
  begin
    Writeln('P1 called!');
  end;
  
  procedure TMyImplClass.P2;
  begin
    Writeln('P2 called!');
  end;
  
  procedure TExternalClass.MyP1AlternativeProcedure;
  begin
    Writeln('MyP1AlternativeProcedure called!');
  end;

var
  LvMyExternalClass: TMyClass;
  LvMyInterface: IMyInterface;
begin
  LvMyExternalClass := TExternalClass.Create;
  LvMyExternalClass.FMyImplClass := TMyImplClass.Create;
  
  LvMyInterface := LvMyExternalClass;
  LvMyInterface.P1;  // calls TMyClass.MyP1AlternativeProcedure;
  LvMyInterface.P2;  // calls TImplClass.P2;
  Readln;
end;

You can remove the following lines from TExternalClass and it's still implementing the interface correctly and compilable!

    procedure IMyInterface.P1 = MyP1AlternativeProcedure;
    procedure MyP1AlternativeProcedure; // and its implementation!

Here is another console-type application sample to make it more clear:


program InterfaceDelegationDemo;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  // Interface definition
  IFirstInterface = interface
    procedure FirstMethod;
  end;

  ISecondInterface = interface
    procedure SecondMethod;
  end;

  // Implementation of the first interface
  TFirstInterfaceImplementation = class(TInterfacedObject, IFirstInterface)
    procedure FirstMethod;
  end;

  // Implementation of the second interface using delegation
  TSecondInterfaceImplementation = class(TInterfacedObject, ISecondInterface)
  private
    FFirstInterfaceImpl: IFirstInterface;
  public
    constructor Create(AFirstInterfaceImpl: IFirstInterface);
    procedure SecondMethod;
  end;

{ TFirstInterfaceImplementation }

procedure TFirstInterfaceImplementation.FirstMethod;
begin
  Writeln('FirstMethod called');
end;

{ TSecondInterfaceImplementation }

constructor TSecondInterfaceImplementation.Create(AFirstInterfaceImpl: IFirstInterface);
begin
  FFirstInterfaceImpl := AFirstInterfaceImpl;
end;

procedure TSecondInterfaceImplementation.SecondMethod;
begin
  Writeln('SecondMethod called');
  FFirstInterfaceImpl.FirstMethod; // Delegating the method call to the first interface implementation
end;

var
  FirstInterfaceImpl: IFirstInterface;
  SecondInterfaceImpl: ISecondInterface;
begin
  FirstInterfaceImpl := TFirstInterfaceImplementation.Create;
  SecondInterfaceImpl := TSecondInterfaceImplementation.Create(FirstInterfaceImpl);

  SecondInterfaceImpl.SecondMethod;
  Readln;
end.


In this code, we have two interfaces, IFirstInterface and ISecondInterface. The TFirstInterfaceImplementation class implements the first interface, and the TSecondInterfaceImplementation class implements the second interface using delegation.

The TSecondInterfaceImplementation class takes an instance of IFirstInterface as a constructor parameter, which represents the first interface implementation. It stores this instance in the FFirstInterfaceImpl field.

When the SecondMethod of TSecondInterfaceImplementation is called, it first prints a message indicating that the method has been called. Then, it delegates the method call to the FirstMethod of the first interface implementation by calling FFirstInterfaceImpl.FirstMethod.

By using this delegation approach, you can separate the implementation of each interface and reuse existing implementations by passing them as parameters to other interface implementations.