delphi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
delphi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

23 Kasım 2019 Cumartesi

Offset Kavramı Ve Dinamik Dizi Yapımı (2 ve 3 Boyutlu)

Daha önce Offset Kavramı ve Dinamik Dizi Yapımı isminde bir yazı yazmış ve işaretçiler(Pointer) başka bir deyiş ile adresler ile neler yapabileceğini izah etmeye çalışmıştım.

Dinamik bir diziyi implement etmede izlediğim yol şuydu;

Dinamik bellek yöneten fonksiyonları kullanarak, hafızada bir alan tahsis (allocate) etmek ve sonra, fonksiyonun döndürdüğü adres üzerinden hesaplama yaparak ilgili veriye ulaşmakdı.


Çıkardığım hesaplama formülü ise aşağıdaki gibiydi;
Basit bir hesap ile formülümüz aşağıdaki gibi olacaktır.
Kaç Bayt = index X Veri tipinin kapladığı alan(Byte)
Yeni Adres = Başlangıç adresi + Kaç Byte

Biraz düşünmeyle bulduğum bu formül, sadece 1 boyutlu bir dizinin elemanlarına erişim formülüydü. Şöyle ki;
Element_Adress = Base_Adress  + (Index * Element_Size)
Memory_Size = Element_Count * Element_Size

Sonrasında ise Tuğrul hocamın (Write Great Code: Volume 1: Write Great Code, Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level) kitaplarını kabaca incelerken, "Composite Data Types and Memory Objects" konusu gözüme çarptı ve doğrudan o konuyu incelemeye başladım.Kadere bakın ki; Pointer'lar, diziler ve çok boyutlu dizilerin elemanlarına erişim formülü yer alıyordu. :)

2 Boyutlu Dizi Ve Elemanlarına Erişim Formülü

Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
Memory_Size = Element_Size * (Col_Count * Row_Count)

3 Boyutlu Dizi ve Elemanlarına Erişim Formülü

Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
Memory_Size = Element_Size * Depth_Size * (Col_Count * Row_Count)

Formülleri biraz açıklayacak olursak;
Element_Adress hafıza bloğundaki verinin adresini temsil etmektedir.
Base_Adress dinamik bellekte alan tahsis eden fonksiyonların döndürdüğü adresi temsil etmektedir.
Memory_Size ise dizinin hafızada kapladığı alanı temsil etmektedir.
Element_Size ise sizeof(veri_tipi) ile bulduğumuz değerdir.
Col_Size ise dizideki kolon sayını temsil eder.
Row_Size ise dizideki satır sayısını temsil eder.
Depth_Size ise kolon ve satır ikilisinden kaç adet olacağını temsil eder.

Formülleri bilmek hoştur amma, uygulayabilmek başkadır başka diyerek formülü, yine pointer kullanımının mümkün olduğunu bildiğim dillerde kabaca denemeye çalıştım.
Formüller aynı şekilde, farklı sözdizimlerinde(syntax) kodlanmıştır.

2 Boyutlu Dinamik Array implementasyonu (C++)

/*
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 17.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
*/
#include <cstdint>
#include <string> 
#include <stdexcept>
#include <sstream>
#include <iostream>

template <class Type>
class DimensionalArray{
private:
   const std::string IndexOutOfRangeException = "IndexOutOfRangeException at "; 
   Type* fBaseAdress;  
   int32_t fColCount;
   int32_t fRowCount;
   int32_t fMemorySize; 
private:
   std::string ToString(int32_t value){
      std::string outString;
      std::stringstream ss;
      ss << value;
      outString = ss.str();
      return outString;
   }

   std::string GetMessage(std::string indexName,int32_t index){
      return std::string(IndexOutOfRangeException + indexName + "["+ ToString(index)+"]");
   }

   void RangeCheck(int32_t colIndex, int32_t rowIndex){
     if ((colIndex < 0) || (colIndex > fColCount - 1))
       throw std::out_of_range(GetMessage("colIndex",colIndex).c_str());
 
     if ((rowIndex < 0) || (rowIndex > fRowCount - 1))
       throw std::out_of_range(GetMessage("rowIndex",rowIndex).c_str());
   }

   int32_t Offset(int32_t colIndex, int32_t rowIndex){
     RangeCheck(colIndex,rowIndex);
     return (colIndex * fRowCount + rowIndex) * sizeof(Type);
   } 

   Type* CalculateElementAdress(int32_t colIndex, int32_t rowIndex){
     return fBaseAdress + Offset(colIndex,rowIndex);
   }

   void CalculateMemorySize(){
     fMemorySize =  sizeof(Type) * (fColCount * fRowCount);
   }
   
   void SetColAndRowCount(int32_t colCount,int32_t rowCount){
      fColCount = colCount;
      fRowCount = rowCount;
   }

   void MemoryAllocate(){
     CalculateMemorySize();
     fBaseAdress = (Type*)malloc(fMemorySize);
   }

   void MemoryFree(){
     free(fBaseAdress);   
   }
public:
  DimensionalArray(int32_t colCount,int32_t rowCount):
    fColCount(colCount),
    fRowCount(rowCount){
    MemoryAllocate();
  }
  ~DimensionalArray() {
    MemoryFree();
  }      
public:
  Type GetElement(int32_t colIndex, int32_t rowIndex){
      Type* elementAdress = CalculateElementAdress(colIndex,rowIndex);
      return *elementAdress;
  }
  
  void SetElement(int32_t colIndex, int32_t rowIndex, Type value){
      Type* elementAdress = CalculateElementAdress(colIndex,rowIndex);
      *elementAdress = value;
  }

  void ReSize(int32_t colCount,int32_t rowCount){
     MemoryFree();
     SetColAndRowCount(colCount,rowCount);
     fMemorySize = CalculateMemorySize();
     fBaseAdress = (Type*)realloc(fBaseAdress,fMemorySize);
  }

  int32_t GetColCount(){
    return fColCount;
  }

  int32_t GetRowCount(){
    return fRowCount;
  }
};


int main(){    
    try{
        DimensionalArray myTwoDimensionArray(2,2); // 2 sütun, 2 satır
        myTwoDimensionArray.SetElement(0,0,31); // 1. kolon, 1. satır.
        myTwoDimensionArray.SetElement(1,0,32); // 2. kolon, 1. satır

        myTwoDimensionArray.SetElement(0,1,33); // 1. kolon, 2. satır
        myTwoDimensionArray.SetElement(1,1,34); // 2. kolon, 2. satır
        
        /*
        int32_t value_0_0 = myTwoDimensionArray.GetElement(0,0); 
        std::cout << "1. kolon, 1. satır Değeri: " << value_0_0 << std::endl;
        int32_t value_1_0 = myTwoDimensionArray.GetElement(1,0); 
        std::cout << "2. kolon, 1. satır Değeri: " << value_1_0 << std::endl;

        int32_t value_0_1 = myTwoDimensionArray.GetElement(0,1); 
        std::cout << "1. kolon, 2. satır Değeri: " << value_0_1 << std::endl;
        int32_t value_1_1 = myTwoDimensionArray.GetElement(1,1);  
        std::cout << "2. kolon, 2. satır Değeri: " << value_1_1 << std::endl;
      */

        // test
        for (size_t colIndex = 0; colIndex < myTwoDimensionArray.GetColCount(); colIndex++){
          for (size_t rowIndex = 0; rowIndex < myTwoDimensionArray.GetRowCount(); rowIndex++){
              int32_t value = myTwoDimensionArray.GetElement(colIndex,rowIndex);
              std::cout << "["<< colIndex << "," << rowIndex <<"]" << value << std::endl;
          }      
        }
    }
    catch(const std::exception& e){
      std::cerr << e.what() << '\n';
    }
    
    system("pause");
    return EXIT_SUCCESS;
}

3 Boyutlu Dinamik Array implementasyonu (C++)

/*
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
*/
#include <cstdint>
#include <string> 
#include <stdexcept>
#include <sstream>
#include <iostream>

template <class Type>
class ThreeDimensionalArray{
private:
   const std::string IndexOutOfRangeException = "IndexOutOfRangeException at "; 
   Type* fBaseAdress;  
   int32_t fColCount;
   int32_t fRowCount;
   int32_t fDepthSize;
   int32_t fMemorySize;
private:
   std::string ToString(int32_t value){
      std::string outString;
      std::stringstream ss;
      ss << value;
      outString = ss.str();
      return outString;
   }

   std::string GetMessage(std::string indexName,int32_t index){
      return std::string(IndexOutOfRangeException + indexName + "["+ ToString(index)+"]");
   }

   void RangeCheck(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
    if ((depthIndex < 0) || (colIndex > fDepthSize - 1))
       throw std::out_of_range(GetMessage("depthIndex",depthIndex).c_str());
 
     if ((colIndex < 0) || (colIndex > fColCount - 1))
       throw std::out_of_range(GetMessage("colIndex",colIndex).c_str());
 
     if ((rowIndex < 0) || (rowIndex > fRowCount - 1))
       throw std::out_of_range(GetMessage("rowIndex",rowIndex).c_str());
   }

   int32_t Offset(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
     RangeCheck(depthIndex,colIndex,rowIndex);    
     return ( (depthIndex * fColCount + colIndex) * fRowCount + rowIndex) * sizeof(Type);
   } 

   Type* CalculateElementAdress(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
     return fBaseAdress + Offset(depthIndex, colIndex, rowIndex);
   }

   void CalculateMemorySize(){
     fMemorySize = sizeof(Type) * fDepthSize * (fColCount * fRowCount);
   }
   
   void SetDepthAndColAndRowCount(int32_t depthSize,int32_t colCount,int32_t rowCount){
      fDepthSize = depthSize;
      fColCount = colCount;
      fRowCount = rowCount;
   }

   void MemoryAllocate(){
     CalculateMemorySize();
     fBaseAdress = (Type*)malloc(fMemorySize);
   }

   void MemoryFree(){
     free(fBaseAdress);   
   }
public:
  ThreeDimensionalArray(int32_t depthSize,int32_t colCount,int32_t rowCount):
    fDepthSize(depthSize),
    fColCount(colCount),
    fRowCount(rowCount){
    MemoryAllocate();
  }
  ~ThreeDimensionalArray() {
    MemoryFree();
  }      
public:
  Type GetElement(int32_t depthIndex,int32_t colIndex, int32_t rowIndex){
      Type* elementAdress = CalculateElementAdress(depthIndex,colIndex,rowIndex);
      return *elementAdress;
  }
  
  void SetElement(int32_t depthIndex,int32_t colIndex, int32_t rowIndex, Type value){
      Type* elementAdress = CalculateElementAdress(depthIndex,colIndex,rowIndex);
      *elementAdress = value;
  }

  void ReSize(int32_t depthSize, int32_t colCount,int32_t rowCount){
     MemoryFree();
     SetDepthAndColAndRowCount(depthSize,colCount,rowCount);
     CalculateMemorySize();
     fBaseAdress = (Type*)realloc(fBaseAdress,fMemorySize);
  }

  int32_t GetColCount(){
    return fColCount;
  }

  int32_t GetRowCount(){
    return fRowCount;
  }

  int32_t GetDepthSize(){
    return fDepthSize;
  }

  int32_t GetMemorySize(){
    return fMemorySize;
  }
};

int main(){    
    ThreeDimensionalArray anArray(2,2,2); // 2 adet, 2 sütun, 2 satırlık tablo

    // access elements ( set)
    for (size_t depthIndex = 0; depthIndex < anArray.GetDepthSize(); depthIndex++)
         for (size_t colIndex = 0; colIndex < anArray.GetColCount(); colIndex++)
             for (size_t rowIndex = 0; rowIndex < anArray.GetRowCount(); rowIndex++)
                     anArray.SetElement(depthIndex,colIndex,rowIndex, depthIndex + colIndex + rowIndex);
 
    // access elements ( get)
    for (size_t depthIndex = 0; depthIndex < anArray.GetDepthSize(); depthIndex++)
         for (size_t colIndex = 0; colIndex < anArray.GetColCount(); colIndex++)
             for (size_t rowIndex = 0; rowIndex < anArray.GetRowCount(); rowIndex++)
                   std::cout <<  anArray.GetElement(depthIndex,colIndex,rowIndex) << std::endl;
  
    system("pause");
    return EXIT_SUCCESS;
}
2 Boyutlu Dinamik Array implementasyonu (delphi)

{
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 17.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
}

unit DimensionalArray;

interface

uses
  Winapi.Windows,
  System.Classes,
  System.SysUtils;


type

  TDimensionalArray = class
   const IndexOutOfRangeException = 'IndexOutOfRangeException at %d';
  type
    PT = ^T;
  strict private
    FColCount: NativeInt;
    FRowCount: NativeInt;
    FMemorySize : NativeInt;
    FBaseAdress: PT;
    FPtrWrapper : TPtrWrapper;
  strict private
    procedure RangeCheck(AColIndex, ARowIndex: NativeInt);
    function Offset(AColIndex, ARowIndex: NativeInt): NativeInt;
    function GetElement(AColIndex, ARowIndex: NativeInt): T;
    procedure SetElement(AColIndex, ARowIndex: NativeInt; const Value: T);
    function CalculateElementAdress(AColIndex, ARowIndex: NativeInt): PT;
    procedure CalculateMemorySize();
    procedure SetColAndRowCount(AColCount, ARowCount: NativeInt);
    procedure MemoryAllocate();
    procedure MemoryFree();
  public
    constructor Create(AColCount, ARowCount: NativeInt);
    destructor Destroy();
  public
    procedure ReSize(AColCount, ARowCount: NativeInt);
  public
    property ColCount: NativeInt read FColCount;
    property RowCount: NativeInt read FRowCount;
    property Element[AColIndex, ARowIndex: NativeInt]: T read GetElement write SetElement; default;
  end;

implementation

{ TDimensionalArray }
constructor TDimensionalArray.Create(AColCount, ARowCount: NativeInt);
begin
  SetColAndRowCount(AColCount,ARowCount);
  MemoryAllocate();
  inherited Create;
end;

destructor TDimensionalArray.Destroy;
begin
  TMarshal.FreeMem(FPtrWrapper);
  inherited Destroy;
end;


procedure TDimensionalArray.RangeCheck(AColIndex, ARowIndex: NativeInt);
begin
  if (AColIndex < 0) or (AColIndex > FColCount-1 ) then
     raise Exception.Create(Format(IndexOutOfRangeException,[AColIndex]));

  if (ARowIndex < 0) or (ARowIndex > FRowCount-1 ) then
     raise Exception.Create(Format(IndexOutOfRangeException,[ARowIndex]));
end;

function TDimensionalArray.Offset(AColIndex, ARowIndex: NativeInt)
  : NativeInt;
begin
  RangeCheck(AColIndex,ARowIndex);
  Result := (AColIndex * FRowCount + ARowIndex) * SizeOf(T);
end;

procedure TDimensionalArray.ReSize(AColCount, ARowCount: NativeInt);
begin
  MemoryFree();
  SetColAndRowCount(AColCount,ARowCount);
  CalculateMemorySize();
  FPtrWrapper := TMarshal.ReallocMem(FPtrWrapper,FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TDimensionalArray.SetColAndRowCount(AColCount, ARowCount: NativeInt);
begin
  FColCount := AColCount;
  FRowCount := ARowCount;
end;

procedure TDimensionalArray.MemoryAllocate();
begin
  CalculateMemorySize();
  FPtrWrapper := TMarshal.AllocMem(FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TDimensionalArray.MemoryFree;
begin
  TMarshal.FreeMem(FPtrWrapper);
end;

function TDimensionalArray.CalculateElementAdress(AColIndex,
  ARowIndex: NativeInt): PT;
begin
  Result := PT(PByte(FBaseAdress) + Offset(AColIndex, ARowIndex));
end;

procedure TDimensionalArray.CalculateMemorySize();
begin
  FMemorySize := SizeOf(T) * (FColCount * FRowCount);
end;

function TDimensionalArray.GetElement(AColIndex, ARowIndex: NativeInt): T;
begin
  Result := CalculateElementAdress(AColIndex, ARowIndex)^;
end;

procedure TDimensionalArray.SetElement(AColIndex, ARowIndex: NativeInt;
  const Value: T);
begin
  CalculateElementAdress(AColIndex, ARowIndex)^ := Value;
end;

end.
3 Boyutlu Dinamik Array implementasyonu (delphi)

{
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
}

unit ThreeDimensionalArray;

interface

uses
  Winapi.Windows,
  System.Classes,
  System.SysUtils;

type

  TThreeDimensionalArray = class
   const IndexOutOfRangeException = 'IndexOutOfRangeException at %s %d';
  type
    PT = ^T;
  strict private
    FDepthSize :NativeInt;
    FColCount: NativeInt;
    FRowCount: NativeInt;
    FMemorySize : NativeInt;
    FBaseAdress: PT;
    FPtrWrapper : TPtrWrapper;
  strict private
    function GetMessage(AIndexName:string;AIndex:NativeInt):string;
    procedure RangeCheck(ADepthIndex,AColIndex, ARowIndex: NativeInt);
    function Offset(ADepthIndex,AColIndex, ARowIndex: NativeInt): NativeInt;
    function GetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt): T;
    procedure SetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt; const Value: T);
    function CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex: NativeInt): PT;
    procedure CalculateMemorySize();
    procedure SetDepthAndColAndRowCount(ADepthSize,AColCount, ARowCount: NativeInt);
    procedure MemoryAllocate();
    procedure MemoryFree();
  public
    constructor Create(ADepthSize,AColCount, ARowCount: NativeInt);
    destructor Destroy();
  public
    procedure ReSize(ADepthSize,AColCount, ARowCount: NativeInt);
  public
    property DepthSize: NativeInt read FDepthSize;
    property ColCount: NativeInt read FColCount;
    property RowCount: NativeInt read FRowCount;
    property MemorySize: NativeInt read FMemorySize;
    property Element[ADepthIndex,AColIndex, ARowIndex: NativeInt]: T read GetElement write SetElement; default;
  end;

implementation

{ TDimensionalArray }
constructor TThreeDimensionalArray.Create(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  SetDepthAndColAndRowCount(ADepthSize,AColCount,ARowCount);
  MemoryAllocate();
  inherited Create;
end;

destructor TThreeDimensionalArray.Destroy;
begin
  TMarshal.FreeMem(FPtrWrapper);
  inherited Destroy;
end;

function TThreeDimensionalArray.GetMessage(AIndexName: string;
  AIndex: NativeInt): string;
begin
   Result := Format(IndexOutOfRangeException,[AIndexName,AIndex]);
end;

procedure TThreeDimensionalArray.RangeCheck(ADepthIndex, AColIndex,
  ARowIndex: NativeInt);
begin
  if (ADepthIndex < 0) or (ADepthIndex > Pred(FDepthSize) ) then
     raise Exception.Create(GetMessage('ADepthIndex',ADepthIndex));

  if (AColIndex < 0) or (AColIndex > Pred(FColCount) ) then
     raise Exception.Create(GetMessage('AColIndex',AColIndex));

  if (ARowIndex < 0) or (ARowIndex > Pred(FRowCount) ) then
     raise Exception.Create(GetMessage('ARowIndex',ARowIndex));
end;

function TThreeDimensionalArray.Offset(ADepthIndex,AColIndex, ARowIndex: NativeInt)
  : NativeInt;
begin
  RangeCheck(ADepthIndex,AColIndex, ARowIndex);
  Result := ( (ADepthIndex * FColCount + AColIndex) * FRowCount + ARowIndex) * SizeOf(T);
end;

procedure TThreeDimensionalArray.ReSize(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  MemoryFree();
  SetDepthAndColAndRowCount(ADepthSize,AColCount,ARowCount);
  CalculateMemorySize();
  FPtrWrapper := TMarshal.ReallocMem(FPtrWrapper,FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TThreeDimensionalArray.SetDepthAndColAndRowCount(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  FDepthSize := ADepthSize;
  FColCount := AColCount;
  FRowCount := ARowCount;
end;

procedure TThreeDimensionalArray.MemoryAllocate();
begin
  CalculateMemorySize();
  FPtrWrapper := TMarshal.AllocMem(FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TThreeDimensionalArray.MemoryFree;
begin
  TMarshal.FreeMem(FPtrWrapper);
end;

function TThreeDimensionalArray.CalculateElementAdress(ADepthIndex,AColIndex,
  ARowIndex: NativeInt): PT;
begin
  Result := PT(PByte(FBaseAdress) + Offset(ADepthIndex,AColIndex, ARowIndex));
end;

procedure TThreeDimensionalArray.CalculateMemorySize();
begin
  FMemorySize := SizeOf(T) * FDepthSize * (FColCount * FRowCount);
end;

function TThreeDimensionalArray.GetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt): T;
begin
  Result := CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex)^;
end;

procedure TThreeDimensionalArray.SetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt;
  const Value: T);
begin
  CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex)^ := Value;
end;

end.
2 Boyutlu Dinamik Array implementasyonu (C#)

/*
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 18.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size

  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/blittable (Unmanaged type constraint)
*/

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

unsafe class DimensionalArray where T : unmanaged
{
    private int colCount;
    private int rowCount;
    private int memorySize;
    private IntPtr baseAdress;

    public DimensionalArray(int colCount, int rowCount)
    {
        SetColAndRowCount(colCount, rowCount);
        MemoryAllocate();
    }

    private int Offset(int colIndex, int rowIndex)
    {
        return (colIndex * rowCount + rowIndex) * Marshal.SizeOf(typeof(T));
    }

    private IntPtr CalculateElementAdress(int colIndex, int rowIndex)
    {
        IntPtr elementAdress = IntPtr.Add(baseAdress, Offset(colIndex, rowIndex));
        return elementAdress;
    }

    private void CalculateMemorySize()
    {
        memorySize = Marshal.SizeOf(typeof(T)) * (colCount * rowCount);
    }

    private void SetColAndRowCount(int colCount, int rowCount)
    {
        this.colCount = colCount;
        this.rowCount = rowCount;
    }

    private void MemoryAllocate()
    {
        CalculateMemorySize();
        baseAdress = Marshal.AllocHGlobal(memorySize);
    }

    private void ReSize(int colCount, int rowCount)
    {
        /*
         cb:IntPtr
         The new size of the allocated block. 
         This is not a pointer; it is the byte count you are requesting, cast to type IntPtr. 
         If you pass a pointer, it is treated as a size.
         https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.reallochglobal?view=netframework-4.8
         */
        MemoryFree();
        SetColAndRowCount(colCount, rowCount);
        CalculateMemorySize();
        baseAdress = Marshal.ReAllocHGlobal(baseAdress, (IntPtr)memorySize);
    }

    private void MemoryFree()
    {
        Marshal.FreeHGlobal(baseAdress);
    }

    public int ColCount { get { return colCount; } }
    public int RowCount { get { return rowCount; } }

    private T* GetElementAdress(int colIndex, int rowIndex)
    {
        void* unTypedElementAdress = CalculateElementAdress(colIndex, rowIndex).ToPointer();
        T* typedElementAdress = (T*)unTypedElementAdress;
        return typedElementAdress;
    }

    public T this[int colIndex, int rowIndex]
    {
        get
        {
            return *GetElementAdress(colIndex, rowIndex);
        }
        set
        {
            *GetElementAdress(colIndex, rowIndex) = value;
        }
    }

    ~DimensionalArray()
    {
        MemoryFree();
    }
}

class Program
{
    static void Main(string[] args)
    {
        DimensionalArray myTwoDimensionArray = new DimensionalArray(2, 2);
        myTwoDimensionArray[0, 0] = 31;
        myTwoDimensionArray[1, 0] = 32;
        myTwoDimensionArray[0, 1] = 33;
        myTwoDimensionArray[1, 1] = 34;

        // test
        for (int colIndex = 0; colIndex < myTwoDimensionArray.ColCount; colIndex++)
        {
            for (int rowIndex = 0; rowIndex < myTwoDimensionArray.RowCount; rowIndex++)
            {
                int value = myTwoDimensionArray[colIndex, rowIndex];
                MessageBox.Show(value.ToString());
            }
        }
    }
}
3 Boyutlu Dinamik Array implementasyonu (C#)

/*
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size

  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/blittable (Unmanaged type constraint)
*/

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

unsafe class ThreeDimensionalArray where T : unmanaged
{
    private const string IndexOutOfRangeException = "IndexOutOfRangeException at %s %d";

    private int depthSize;
    private int colCount;
    private int rowCount;
    private int memorySize;
    private IntPtr baseAdress;

    public ThreeDimensionalArray(int depthSize, int colCount, int rowCount)
    {
        SetDepthAndColAndRowCount(depthSize, colCount, rowCount);
        MemoryAllocate();
    }

    private string GetMessage(string indexName, int index)
    {
        return string.Format(IndexOutOfRangeException, indexName, index);
    }

    private void RangeCheck(int depthIndex, int colIndex, int rowIndex)
    {
        if ((depthIndex < 0) || (colIndex > depthSize - 1))
            throw new Exception(GetMessage("depthIndex", depthIndex));

        if ((colIndex < 0) || (colIndex > colCount - 1))
            throw new Exception(GetMessage("colIndex", colIndex));

        if ((rowIndex < 0) || (rowIndex > rowCount - 1))
            throw new Exception(GetMessage("rowIndex", rowIndex));
    }

    private int Offset(int depthIndex, int colIndex, int rowIndex)
    {
        RangeCheck(depthIndex, colIndex, rowIndex);
        return ((depthIndex * colCount + colIndex) * rowCount + rowIndex) * Marshal.SizeOf(typeof(T));
    }

    private IntPtr CalculateElementAdress(int depthIndex, int colIndex, int rowIndex)
    {
        IntPtr elementAdress = IntPtr.Add(baseAdress, Offset(depthIndex, colIndex, rowIndex));
        return elementAdress;
    }

    private void CalculateMemorySize()
    {
        memorySize = Marshal.SizeOf(typeof(T)) * this.depthSize * (this.colCount * this.rowCount);
    }

    private void SetDepthAndColAndRowCount(int depthSize, int colCount, int rowCount)
    {
        this.depthSize = depthSize;
        this.colCount = colCount;
        this.rowCount = rowCount;
    }

    private void MemoryAllocate()
    {
        CalculateMemorySize();
        baseAdress = Marshal.AllocHGlobal(memorySize);
    }

    private void ReSize(int depthSize, int colCount, int rowCount)
    {
        /*
         cb:IntPtr
         The new size of the allocated block. 
         This is not a pointer; it is the byte count you are requesting, cast to type IntPtr. 
         If you pass a pointer, it is treated as a size.
         https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.reallochglobal?view=netframework-4.8
         */
        MemoryFree();
        SetDepthAndColAndRowCount(depthSize, colCount, rowCount);
        CalculateMemorySize();
        baseAdress = Marshal.ReAllocHGlobal(baseAdress, (IntPtr)memorySize);
    }

    private void MemoryFree()
    {
        Marshal.FreeHGlobal(baseAdress);
    }

    public int DepthSize { get { return depthSize; } }
    public int ColCount { get { return colCount; } }
    public int RowCount { get { return rowCount; } }
    public int MemorySize { get { return memorySize; } }

    private T* GetElementAdress(int depthIndex, int colIndex, int rowIndex)
    {
        void* unTypedElementAdress = CalculateElementAdress(depthIndex, colIndex, rowIndex).ToPointer();
        T* typedElementAdress = (T*)unTypedElementAdress;
        return typedElementAdress;
    }

    public T this[int depthIndex, int colIndex, int rowIndex]
    {
        get
        {
            return *GetElementAdress(depthIndex, colIndex, rowIndex);
        }
        set
        {
            *GetElementAdress(depthIndex, colIndex, rowIndex) = value;
        }
    }

    ~ThreeDimensionalArray()
    {
        MemoryFree();
    }
}

class Program
{
    static void Main(string[] args)
    {
        ThreeDimensionalArray anArray = new ThreeDimensionalArray(2, 2, 2);
        //anArray.MemorySize;

        // set values
        for (int depthIndex = 0; depthIndex < anArray.DepthSize; depthIndex++)
            for (int colIndex = 0; colIndex < anArray.ColCount; colIndex++)
                for (int rowIndex = 0; rowIndex < anArray.RowCount; rowIndex++)
                    anArray[depthIndex, colIndex, rowIndex] = depthIndex + colIndex + rowIndex;

        // access elements
        for (int depthIndex = 0; depthIndex < anArray.DepthSize; depthIndex++)
        {
            for (int colIndex = 0; colIndex < anArray.ColCount; colIndex++)
            {
                for (int rowIndex = 0; rowIndex < anArray.RowCount; rowIndex++)
                {
                    int value = anArray[depthIndex, colIndex, rowIndex];
                }
            }
        }
    }
}
Güncel proje dosyalarına erişim :
Dynamic Array Implementation[Dimensional]
Dynamic Array Implementation[Three-Dimensional]

12 Temmuz 2019 Cuma

Dinamik Olarak Oluşturan Kodu Çalıştırmak ve JIT mimarisine Düşük Seviyeli Bir Bakış

Hafıza yönetim API'lerinden olan VirtualAlloc fonksiyonunun dokümantasyonunda aşağıdaki gibi açıklama var.

To execute dynamically generated code, use VirtualAlloc to allocate memory and the VirtualProtect function to grant PAGE_EXECUTE access.

Burada hepimizin anlayacağı üzere diyor ki;
Dinamik olarak oluşturulan kodu çalıştırmak için, VirtualAlloc fonksiyonu ile hafıza da tahsis yap ve VirtualProtect fonksiyonu ile de PAGE_EXECUTE yetkisi ver.

Tamam da burada dinamik olarak oluşturulan kod nasıl bir kod olmalı? Java , Delphi, C, C++, C# yoksa Javascript ya da başka bir programlama dili kodu mu?
Elbette hiçbiri değil, CPU'nun anlayacağı kodlar olmalı.

Öyle ise; CPU'nun anlayacağı kodları oluşturup, çalıştırmaya çalışalım. 5 + 4 işlemini CPU'nun anlayacağı şekle dönüştürüp, çalıştırmasını isteyeceğiz.

mov eax,5
add eax,4
ret
Yukarıdaki kodda eax register'a 5 değerini yazıyoruz.Daha sonra ise add komutu ile de üzerine 4 ekliyoruz. ret komutu ile de eax register'daki son değeri geriye döndürüyoruz.



Yanlız buraya kadar yazdığımız kodlar yine CPU'nun anlayabileceği kodlar değil. Yukarıda yazdığımız mov eax,5 ... gibi sembolik kodları VirtualAlloc fonksiyonu ile çalıştırmak istersek yine çalıştıramayacağız.

77C3DBA8 | B8 05 00 00 00           | mov eax,5                               |
77C3DBAD | 83 C0 04                 | add eax,4                               |
77C3DBB0 | C3                       | ret                                     |

Delphi Byte Array

Array [1..9] of Byte = (
$B8, $05, $00, $00, $00, $83, $C0, $04, $C3
);

C/C++ Byte Array

{
0xB8, 0x05, 0x00, 0x00, 0x00, 0x83, 0xC0, 0x04, 0xC3
};
O nedenle yukarıdaki görselde görüleceği üzere asm kodları yerine, byte karşılığını alıp, VirtualAlloc fonksiyonu ile hafıza da tahsis ettiğimiz alana yerleştireceğiz, sonrasında flProtect parametresine de Memory Protection Constants değerlerinden PAGE_EXECUTE ile başlayan değerlerden birini parametre olarak geçeceğiz. O zaman bu cek-caklı vaatlerimizi bir kenara bırakıp, kodlamaya başlayalım.

Aşağıda işletim sistemi api'lerinin kullanımına olanak veren, bildiğim dillerde hazırladığım örnekleri inceleyebilirsiniz.

C#

using System;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;

unsafe class Program
{
    // winnt.h    
    const int MEM_COMMIT = 0x00001000;
    const int MEM_RESERVE = 0x00002000;
    const int MEM_RELEASE = 0x00008000;
    const int PAGE_EXECUTE_READWRITE = 0x40;

    [DllImport("kernel32.dll", SetLastError = true)]
    unsafe static extern IntPtr VirtualAlloc(void* lpAddress, UIntPtr dwSize, int flAllocationType, int flProtect);

    [DllImport("kernel32.dll", SetLastError = true)]
    unsafe static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, int dwFreeType);


    public delegate int FunctionPtr();
    static byte[] ByteCode = new byte[] { 0xB8, 0x05, 0x00, 0x00, 0x00, 0x83, 0xC0, 0x04, 0xC3 };

    const string FileName = "ByteCode.bin";
    const string ByteCodeURL = "https://github.com/ismailkocacan/Experiment/tree/master/JIT_ExecuteDynamicallyGeneratedCode/tests/" + FileName;

    static void Main(string[] args)
    {
        IntPtr P  = VirtualAlloc(null, (UIntPtr)ByteCode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        Marshal.Copy(ByteCode, 0, P, ByteCode.Length);
        FunctionPtr functionPtr = (FunctionPtr)Marshal.GetDelegateForFunctionPointer(P, typeof(FunctionPtr));
        int result = functionPtr();
        VirtualFree(P, (UIntPtr)ByteCode.Length, MEM_RELEASE);
    }

    static byte[] GetByteCode()
    {
        WebClient client = new WebClient();
        byte[] codeData = client.DownloadData(ByteCodeURL);
        return codeData;
    }

    static void SaveToFile()
    {
        File.WriteAllBytes(FileName, ByteCode);
    }
}


C

#include <Windows.h>

typedef int(*FunctionPtr)();

byte BYTE_CODE[] = { 0xB8, 0x04, 0x00, 0x00, 0x00, 0x83, 0xC0, 0x03, 0xC3 };
const int CODE_SIZE = sizeof BYTE_CODE;

int main()
{
 PVOID P = VirtualAlloc(NULL, CODE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 memcpy(P, BYTE_CODE, CODE_SIZE);
 FunctionPtr functionPtr = (FunctionPtr)P;
 int result = functionPtr();
 VirtualFree(P, CODE_SIZE, MEM_RELEASE);
}

Delphi

program Project2;
 
{$APPTYPE CONSOLE}
{$R *.res}
 
uses
 Winapi.Windows,
 System.SysUtils;
 
type
 TFunctionPtr = function: Integer;
 
const
 BYTE_CODE: Array [1 .. 9] of Byte = ($B8, $05, $00, $00, $00, $83, $C0, $04, $C3);
 CODE_SIZE = sizeof(BYTE_CODE);
 
var
 P: Pointer;
 Result: Integer;
 FunctionPtr: TFunctionPtr;
begin
 P := VirtualAlloc(nil, CODE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 CopyMemory(P, @BYTE_CODE, CODE_SIZE);
 FunctionPtr := TFunctionPtr(P);
 Result := FunctionPtr();
 VirtualFree(P, CODE_SIZE, MEM_RELEASE);
end.

Yukarıda yaptıklarımızı kısaca açıklayacak olursak;
- VirtualAlloc ile hafızada CODE_SIZE array'in boyu kadar yer açtık.

- PAGE_EXECUTE_READWRITE değerini geçerek bu hafıza bloğunun çalıştırabilir, okunup , yazılabilir olduğunu belirttik.

- CopyMemory ile çalıştırmak istediğimiz kodları hafızaya yerleştirdik.

- VirtualAlloc bize hafızada tahsis ettiği bloğunun başlagıç pointer'ını döndürmüştü.O adreside bir fonksiyon pointer'ına cast edip, çağırdık.

Konunun başlığı "Dinamik Olarak Oluşturan Kodu Çalıştırmak ve JIT mimarisine Düşük Seviyeli Bir Bakış" yazıyor ama ben debugger'da kodu kendim manuel yazdım.
Bu nasıl dinamik oluşturma diyebilirsiniz.Evet haklısınız.
Şimdi kodu dinamik olarak bize oluşturan bir Assembler'a ihtiyacımız var...
"Burada biz daha çok çalıştırma kısmını incelemiş olduk."

Dahası; .Net ve Java gibi run-time bağlı dillerde kullanılan bir teknik. Derlenmiş MSIL ya da Java Byte kodu tekrar CPU'nun anlayacağı koda dönüştürüp çalıştırma işi, işte bu şekilde gerçekleşiyor.Başka bir deyişle "JIT Compile" diye ifade edilmekte...

Şuanda aklıma gelen, gelmeyen pek çok farklı amaç için kullanılabilir.
Injection, dağıtık kod, sanal makine vs.
Kaynak kodlara buradan ulaşabilirsiniz.

8 Eylül 2018 Cumartesi

Offset Kavramı ve Dinamik Dizi Yapımı

Birçok programlama dilinde dinamik dizi kullanabiliyoruz.Dizilerde [] indexer operator yardımıyla dizi elemanlarına erişebiliyoruz.
C++

   int* myArray = new int[2];
   myArray[0] = 10;
   delete[] myArray;
C#

   int[] myArray = new int[2];
   myArray[0] = 10;
Java

   int[] myArray = new int[2];
   myArray[0] = 10;
Delphi

var
  MyArray: array of Integer;
begin
  SetLength(MyArray, 2);
  MyArray[0] := 10;
end.
Yukarıdaki örnekleri incelediğimizde C++, C#, Java gibi dillerde new operatörü, delphi için ise SetLength fonksiyonu yardımıyla dinamik dizileri kullanabiliyoruz.

C dilinde ise; C++, C#, java da bulunan new operatoru ya da delphi'deki gibi bir SetLength fonksiyonu bulunmuyor.Dolayısı ile dinamik dizimizi kendimiz gerçekleştirmemiz gerekiyor.
Aşağıdaki örnekte; 8 byte'lık bir hafıza bloğu tahsis edilip, myArray işaretçisinin [] indexer operatörü yardımıyla değerlere erişiyoruz.

 int* myArray = malloc(sizeof(int) * 2);
 myArray[0] = 10;
 myArray[1] = 20;
 free(myArray);
Sanırım nihayet konuya giriş yapabilmek için gerekli alt yapıyı sağlamış oldum :)

Peki dil C, C++, C#, java ya da delphi ya da bir başka dil farketmeksizin myArray[index] operatorü ile dizi elemanlarına erişmek istediğimde, hafıza bloğunda ilgili index'deki veriye nasıl eriştiğini biliyor muyum ? Bilmiyorum.



Bu yazıda dil seviyesinde index mantığını nasıl gerçekleştirebiliriz bunu anlamaya ve öğrenmeye çalışacağız. Bilindiği üzere C, C++, C#, Delphi gibi dillerde, işaretçileri(pointer), işaretçi aritmetiğini(pointer math) kullanabiliyoruz.Java'da ise yine bildiğim kadarıyla şuan için işaretçi kullanımına dil seviyesinde izin verilmiyor.

İşaretçi(pointer) kullanımına izin veren dillerde index'leme mantığını işaretçiler üzerinden verinin adresini hesaplayarak gerçekleştireceğiz.

Dinamik bellek yöneten fonksiyonlar veriyi hafızada tahsis ettikten sonra geriye, veri bloğunun başlangıç adresini untyped pointer(void* veya PVOID veya LPVOID veya Pointer veya IntPtr) tipinde döndürmektedir. C

void *        _RTLENTRY _EXPFUNC malloc(_SIZE_T __size);
Windows API

WINBASEAPI
_Ret_maybenull_
_Post_writable_byte_size_(dwBytes)
DECLSPEC_ALLOCATOR
LPVOID
WINAPI
HeapAlloc(
    _In_ HANDLE hHeap,
    _In_ DWORD dwFlags,
    _In_ SIZE_T dwBytes
    );
C#

IntPtr p = Marshal.AllocHGlobal(size);
Delphi

procedure GetMem(var P: Pointer; Size: Integer);

function HeapAlloc(hHeap: THandle; dwFlags, dwBytes: DWORD): Pointer; stdcall;
UnTyped Pointer'larda aritmetik işlemlere izin verilmediği için, fonksiyonlardan dönen veri tipini typed pointer'lara çevireceğiz. Örneğin C dilinde aşağıdaki gibi untyped pointer tipinde ki p değişkeni için p += 4; işlemine izin verilmeyecektir.

void* p = malloc(sizeof(int) * 2);
p += 4;
[bcc32 Error] main.c(17): E2453 Size of the type 'void' is unknown or zero
Error C2036 'void *': unknown size

Örneğin Delphi dili için aşağıdaki gibi untyped pointer tipide ki P := P + 4; ya da Inc(P,4); işlemine izin verilmeyecektir.

var
  P: Pointer;
begin
  GetMem(P, SizeOf(Integer) * 2);
  P := P + 4;
end.
[dcc32 Error] Project2.dpr(60): E2015 Operator not applicable to this operand type
ya da

var
  P: Pointer;
begin
  GetMem(P, SizeOf(Integer) * 2);
  Inc(P, 4);
end.
[dcc32 Error] Project2.dpr(70): E2001 Ordinal type required
Dolayısı ile hafıza adreslerini index yoluyla hesaplayabilmemiz için typed pointerları kullanmamız gerekiyor.
C

 int* p = malloc(sizeof(int) * 2);
 *p = 10;
  p += 4;
 *p = 20;
free(p);
Delphi

var
  P: ^Integer; // ya da PInteger
begin
  GetMem(P, SizeOf(Integer) * 2);
  P^ := 10;
  Inc(P, 4);
  P^ := 20;
  FreeMem(P, SizeOf(Integer) * 2);
end;
C#

public static IntPtr Add(IntPtr pointer, int offset);
C#'da ise IntPtr(Pointer ya da Handle veri tipi için) yapısında bulunan Add methodu yardımıyla hesaplama yapabiliyoruz.

static void Main(string[] args)
{
  IntPtr p = Marshal.AllocHGlobal(sizeof(int) * 2);
  Marshal.WriteInt32(p, 10);
  IntPtr newP = IntPtr.Add(p, 4);
  Marshal.WriteInt32(newP, 20);
  Marshal.FreeHGlobal(p);
}
Yukarıdaki gibi hafıza bloğunun başlangıç adresini gösteren bir işaretçi üzerinde "doğrudan" değişiklik yapan p += 4; ya da P := P + 4; gibi aritmetik işlemler yazacağımız index'leme mantığı için çok doğru olmayabilir.O nedenle dinamik bellekte veriye işaret eden işaretçi üzerinde doğrudan bir değişiklik yapmadan yeni adresi hesaplayabiliriz.

Basit bir hesap ile formülümüz aşağıdaki gibi olacaktır.
Kaç Bayt = index X Veri tipinin kapladığı alan(Byte)
Yeni Adres = Başlangıç adresi + Kaç Byte

Kabaca başlangıç adresine kaç byte ekleyeceğimizi index yoluyla hesaplayarak verinin offset'ini hesaplamış olduk. Formülü kodlayacak olursak;
C

int index = 0;
int* p = malloc(sizeof(int) * 2);
int* newp = p + (index * sizeof(int));
*newp = 10;
free(p);
Delphi

var
  P, NewP: PInteger;
  Index: Integer;
begin
  Index := 0;
  GetMem(P, SizeOf(Integer) * 2);
  NewP := PInteger(PByte(P) + (Index * SizeOf(Integer)));
  NewP^ := 10;
  FreeMem(P, SizeOf(Integer) * 2);
end;
index değişkeninin değerini değiştirerek dizinin elemanlarına erişim sağlayabiliriz. Yazdığımız kodları eleştirecek olursak; Kodumuz çok da yakışıklı olmadı.
Örneğin; index * sizeof(int) yerine bir offset fonksiyonu yazabiliriz. C

int Offset(int index){
  return index * sizeof(int);
}
int* newp = p + Offset(index);
Delphi

function Offset(Index: Integer): Integer;
begin
  Result := Index * SizeOf(Integer);
end;
NewP := PInteger(PByte(P) + Offset(Index));
Dizinin eleman sayısından, hafızada tahsis edilecek veri bloğununun kaplayacağı alanı hesaplayan bir fonksiyon yazılabilir.
C

int* p = malloc(sizeof(int) * 2);
yerine

int MemSize(int count) {
   return sizeof(int) * count;
}
int* p = malloc(MemSize(2));
Delphi

GetMem(P, SizeOf(Integer) * 2); 
yerine

function MemSize(Count: Integer): Integer;
begin
  Result := SizeOf(Integer) * Count;
end;
GetMem(P, MemSize(2));
Eleştirmeye devam edecek olursak;
Şuana kadar yazdığımız kodların tamamı int veri tipi üzerine idi. Programlama sadece int veri tipinden ibaret değil ki ?
Başka veri tipleri için ne yapacağız ?
Aynı mantığı her veri tipi için tekrar tekrar mı yazacağız ?

İşte tam bu noktada, algoritmayı veri tipinden soyutlamak ya da bağımsız kılmak için, nesne yönelimli programlamanın sağladığı faydalardan yararlanabiliriz. Bunun için C++'da templates, C# ve Delphi'de generics'ler ile daha esnek kodlamalar yapabiliriz.Generics ve templates ile her ne kadar veri tipini esnek hale getirmiş olsak da, algoritmamız her veri tipi için uygun olmayabilir.Bu nokta da generics constraint'ler ile belli tipler için kurallar getirebiliriz. Tabi ki yine C'de untyped pointerlar ile daha generic fonksiyonlar mümkün.

Bahsettiğim generic haline Dynamic Array Implementation adresinden erişebilir, katkı sağlayabilir (mümkünse), gördüğünüz eksik ya da yanlışlar konusunda bilgilendirirseniz sevinirim.

Sağlıcakla.

1 Eylül 2018 Cumartesi

Calling Convention Kavramı / Fonksiyon Çağırım Düzeni

Fonksiyon çağırım düzeni (Calling Convention kısaca CC diyelim) özetle;
fonksiyon ya da procedure parametrelerinin hangilerinin CPU register veya stack'de, hangi sıra ile tahsis edileceğidir.

Delphi, C/C++ gibi dillerde fonksiyon ya da procedure'e ekleyeceğimiz CC ile (register, pascal, cdecl, stdcall, safecall, winapi, __fastcall gibi) fonksiyonun çağırım düzeni belirtilebilir.

Delphi'de varsayılan CC register'dır. Yani fonksiyon ya da procedure'nin sonuna herhangi CC belirtilmediğinde varsayılan olarak register çalışır.Stack frame oluşturmak yerine, ECX, EDX, EAX gibi CPU register'ları kullandığı için, fonksiyonun çalışma hızı en efektif ve hızlı olanıdır.Aşağıdaki fonksiyonda X ve Y parametreleri stack yerine CPU register'larda tahsis edilecektir.

function Hesapla(X, Y: Integer): Integer; 
function Hesapla(X, Y: Integer): Integer; register;
Yukarıdaki her iki fonksiyon da aynı CC sahiptir diyebiliriz.
C/C++'da varsayılan CC cdecl'dir. cdecl adı üstünde c decleration kısaltmasıdır.Tüm parametreler stack'de tutulur. C/C++

int Hesapla(int X, int Y);
int __cdecl Hesapla(int X, int Y);  
Delphi

function Hesapla(X, Y: Integer): Integer; cdecl;
Yukarıdaki bilgilere göre;
Varsayılan bir C fonksiyonu ile, varsayılan bir delphi fonksiyonunu karşılaştırdığımızda, mantıken ve de teknik olarak delphi fonksiyonun daha hızlı çalışması gerekir.
Çünkü C'de "varsayılan olarak", fonksiyon parametreleri stack'de tahsis ediliyorken, delphi'de 3 adet parametreye kadarı CPU register'larda geriye kalanlar ise stack'de tahsis edilir. CPU register'ları stack'den daha hızlıdır.

C'de delphi'de olduğu gibi parametreleri stack yerine CPU register'da tahsis etmek için __fastcall CC kullanılır. Kısaca C'nin __fastcall'ı ile Delphin'nin register CC'ı aynıdır diyebiliriz. Delphi

function Hesapla(X, Y: Integer): Integer; register;
begin
end;
Hesapla(1,2);
ASM Çıktısı

Project1.dpr.45: Hesapla(1,2);
00419500 BA02000000       mov edx,$00000002
00419505 B801000000       mov eax,$00000001
0041950A E881DDFFFF       call Hesapla
C/C++

int __fastcall Hesapla(int X, int Y) {
}
Hesapla(1,2);
ASM Çıktısı

main.cpp.48: Hesapla(1,2);
0040125B BA02000000       mov edx,$00000002
00401260 B801000000       mov eax,$00000001
00401265 E8DEFFFFFF       call Hesapla(int,int)
Dokümanlar winapi ya da stdcall'ın aslında bir CC olmadığını söylüyor.
Daha çok windows işletim sistemindeki API'lerde bu CC görebiliriz. winwindef.h içerisinde tanımı şöyle;
#define WINAPI __stdcall
Örneğin CreateFile fonksiyonun tanımında görebiliriz. fileapi.h

WINBASEAPI
HANDLE
WINAPI
CreateFileA(
   _In_ LPCSTR lpFileName,
   _In_ DWORD dwDesiredAccess,
   _In_ DWORD dwShareMode,
   _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
   _In_ DWORD dwCreationDisposition,
   _In_ DWORD dwFlagsAndAttributes,
   _In_opt_ HANDLE hTemplateFile
   );
Delphi zaten bir çok windows api fonksiyonunu hali hazırda bize WinApi.Windows.pas uniti içersinde sunuyor.

function CreateFileA(lpFileName: LPCSTR; dwDesiredAccess, dwShareMode: DWORD;
 lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
 hTemplateFile: THandle): THandle; stdcall;
function CreateFileA; external kernelbase name 'CreateFileA';
Yine dokümanlar pascal CC'nin ise geriye dönük uyumluluk için korumaya devam edildiğini söylüyor. Şimdi örnek procedure ya da fonksiyonların CPU penceresindeki kodunu inceleyelim.
(View -> Debug Windows -> CPU Windows -> Entire CPU) ya da CTRL + ALT + C

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses

 System.SysUtils;

procedure Procedure1(A, B, C, D, E: Integer);
begin
end;

procedure Procedure2(A, B, C, D, E: Integer); register;
begin
end;

procedure Procedure3(A, B, C, D, E: Integer); pascal;
begin
end;

procedure Procedure4(A, B, C, D, E: Integer); cdecl;
begin
end;

procedure Procedure5(A, B, C, D, E: Integer); stdcall;
begin
end;

procedure Procedure6(A, B, C, D, E: Integer); safecall;
begin
end;

begin
 Procedure1(1, 5, 7, 9, 10);
 Procedure2(1, 5, 7, 9, 10);
 Procedure3(1, 5, 7, 9, 10);
 Procedure4(1, 5, 7, 9, 10);
 Procedure5(1, 5, 7, 9, 10);
 Procedure6(1, 5, 7, 9, 10);
end.

Project1.dpr.40: Procedure1(1, 5, 7, 9, 10);
00419500 6A09             push $09
00419502 6A0A             push $0a
00419504 B907000000       mov ecx,$00000007
00419509 BA05000000       mov edx,$00000005
0041950E B801000000       mov eax,$00000001
00419513 E878DDFFFF       call Procedure1
Project1.dpr.41: Procedure2(1, 5, 7, 9, 10);
00419518 6A09             push $09
0041951A 6A0A             push $0a
0041951C B907000000       mov ecx,$00000007
00419521 BA05000000       mov edx,$00000005
00419526 B801000000       mov eax,$00000001
0041952B E878DDFFFF       call Procedure2
Project1.dpr.42: Procedure3(1, 5, 7, 9, 10);
00419530 6A01             push $01
00419532 6A05             push $05
00419534 6A07             push $07
00419536 6A09             push $09
00419538 6A0A             push $0a
0041953A E881DDFFFF       call Procedure3
Project1.dpr.43: Procedure4(1, 5, 7, 9, 10);
0041953F 6A0A             push $0a
00419541 6A09             push $09
00419543 6A07             push $07
00419545 6A05             push $05
00419547 6A01             push $01
00419549 E87ADDFFFF       call Procedure4
0041954E 83C414           add esp,$14
Project1.dpr.44: Procedure5(1, 5, 7, 9, 10);
00419551 6A0A             push $0a
00419553 6A09             push $09
00419555 6A07             push $07
00419557 6A05             push $05
00419559 6A01             push $01
0041955B E870DDFFFF       call Procedure5
Project1.dpr.45: Procedure6(1, 5, 7, 9, 10);
00419560 6A0A             push $0a
00419562 6A09             push $09
00419564 6A07             push $07
00419566 6A05             push $05
00419568 6A01             push $01
0041956A E869DDFFFF       call Procedure6
0041956F E8C4F1FEFF       call @CheckAutoResult
Project1.dpr.46: end.
main.cpp

#pragma hdrstop

#pragma argsused

#ifdef _WIN32

#include 

#else

typedef char _TCHAR;

#define _tmain main

#endif

#include 

void Procedure1(int A, int B, int C, int D, int E) {

}

void __fastcall Procedure2(int A, int B, int C, int D, int E) {

}

void pascal Procedure3(int A, int B, int C, int D, int E) {

}

void __cdecl Procedure4(int A, int B, int C, int D, int E) {

}

void __stdcall Procedure5(int A, int B, int C, int D, int E) {

}

void Procedure6(int A, int B, int C, int D, int E) {

}

int _tmain(int argc, _TCHAR* argv[]) {

  Procedure1(1, 5, 7, 9, 10);
  Procedure2(1, 5, 7, 9, 10);
  Procedure3(1, 5, 7, 9, 10);
  Procedure4(1, 5, 7, 9, 10);
  Procedure5(1, 5, 7, 9, 10);
  Procedure6(1, 5, 7, 9, 10);
  return 0;
}

Yukarıdaki çıktıları incelediğimizde, parametrelerin hangi sırayla (sağdan sola veya soldan sağa doğru) stack'e ittirildiğini(push) ya da hangi CPU register'da (ECX,EDX, EAX) tahsis edildiğini somut bir şekilde görebiliriz. Delphi, C/C++ gibi dillerde fonksiyonlarda çağırım düzenini kısaca bu şekilde ifade edebiliriz.

.net dillerinde ise fonksiyon çağırım düzeni, DLLImport attribute sınıfında bulunan CallingConvention özelliği kullanarak belirtilebilir.Bu tür managed dillerden, umanaged system fonksiyonlarını çağırmanın diğer bir adı ise Platform Invoke ya da kısaca P/Invoke diye ifade edilir.

[DllImport("User32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, int uType);

static void Main(string[] args)
{
   MessageBox(IntPtr.Zero, "Bu bir mesajdır", "Bu da bir başlıktır", 0);
}
Java'da ise "bildiğim kadarıyla" "dil seviyesinde" .net'deki DllImport'a benzer bir sınıf yok. Aşağıdaki kaynaklara göz atabilirsiniz.
x86 calling conventions
Calling Conventions
Pitfalls of converting
Delphi Language Guide (Delphi for Microsoft Win32 Delphi for the Microsoft .NET Framework)

2 Eylül 2013 Pazartesi

Rtti Gevşek Unitler :)

Bir projedeki unit'lerin uses bloglarına baktığımızda bir sürü unit ismi görürüz.

Bunların bir çoğu da projeye bizim eklediğimiz unit dosyalarıdır.

Ve hepsi birbirine bağlıdır.Bu bağımlığı en aza indirmenin yollarından birisi de ,reflection kullanmaktır. :)

Aşağıda yazacağım rttiFormActivator methodu ile bir "unitismi.formsınıfadı" vererek formu oluşturmak için kullanabiliriz.

Bu method kullanıma göre özelleştirebilir.

function  rttiFormActivator(typeQualifiedName : string; isModal:Boolean=false) : TValue;
var
 context      : TRttiContext;
 rttTyp       : TRttiType;
 instanceType : TRttiInstanceType;
 instance     : TValue;
begin

 try
   context   := TRttiContext.Create();
   rttTyp    := context.FindType(typeQualifiedName);

   instanceType := rttTyp.AsInstance;
   instance     := instanceType.GetMethod('Create').Invoke(instanceType.MetaclassType,[nil]) ;

   Result := instance;

   case isModal of
      true  :   instanceType.GetMethod('ShowModal').Invoke(instance,[]);
      false :   instanceType.GetMethod('Show').Invoke(instance,[]);
   end;

 except on E: Exception do
   raise Exception.Create(typeQualifiedName+ ' Tipi register edilmemiş !');
 end;

end;
Unit1.pas dosyanın uses bloğuna kullanmak istediğimiz herhangi bir unit içersindeki formun unitini tanımlamadan direk aşağıdaki kullanılabilecektir.
procedure TForm1.btnShowForm2Click(Sender: TObject);
begin
  rttiFormActivator('Unit2.TForm2',true);
end;


procedure TForm1.btnShowForm3Click(Sender: TObject);
begin
  rttiFormActivator('Unit3.TForm3');
end;
unit1.pas'ın uses bloğuna unit2,unit3 eklemeyerek,bu unitlere bağlı olmasını engellemiş ve dinamik bir kullanım elde etmiş olduk. Yanlız şöyle önemli bir durum var. Kullanacağınız sınıflarını register etmediğiniz zaman rtti amca findType methodu ile ilgili verdiğiniz tipi bulamıyor. o yüzden sınıfları bulunduğu unit içinde register ederek,findType methodu ile bulmasını sağlayabiliriz.
initialization
 RegisterClass(TForm2);

finalization
 UnRegisterClass(TForm2);


Kaynak Kod : https://github.com/ismailkocacan/Rtti-Loose-Coupling-Units

iyi çalışmalar.

9 Mayıs 2013 Perşembe

Generic Listte En Büyük Nesneyi Bulmak !

Biliyorum çok boktan bir başlık oldu.
Aşağıda TKisi recordu ve TKisi recorlarından oluşan generic bir listimiz var(TKisiList).
Amacımız bu TKisiList de ki "id" değeri en büyük olan TKisi instance'ı bulmak(!).

Bunun için TKisiList sınıfına getMaxIdKisi() methodu ekleyip listenin içindeki her bir elemanın id değerini karşılaştırarak id değeri en büyük olan kişi nesnesini buluyoruz.


unit Kisi;

interface

uses
  System.Generics.Defaults,
  System.Generics.Collections;

type
  TKisi = record
    adi: string;
    soyadi: string;
    id: Integer;
  end;

  TKisiList = class(TList)
  public
    function getMaxIdKisi(): TKisi;
  end;

implementation

{ TKisiList }
function TKisiList.getMaxIdKisi: TKisi;
var
  comparer: IComparer;
  i, c: Integer;
begin
  comparer := TComparer.Default;
  c := Self.Items[0].id;
  for i := 1 to Self.Count - 1 do
  begin
    if comparer.Compare(Self[i].id, c) > 0 then
    begin
      c := Self[i].id;
      Result := Self[i];
    end;
  end;
end;

end.


var
  Form3: TForm3;

implementation

uses Kisi;

{$R *.dfm}

procedure TForm3.FormCreate(Sender: TObject);
var
  kisi: TKisi;
  kisiList: TKisiList;

  idEnbuyukKisi:TKisi;
begin
  kisiList:=TKisiList.Create;
  kisi.id:=30;
  kisi.adi:='İSMAİL';
  kisi.soyadi:='KOCACAN';
  kisiList.Add(kisi);


  kisi.id:=20;
  kisi.adi:='KEMAL';
  kisi.soyadi:='BAYAT';
  kisiList.Add(kisi);

  kisi.id:=31;
  kisi.adi:='HAYDAR';
  kisi.soyadi:='DÜMEN';
  kisiList.Add(kisi);


  idEnbuyukKisi:=kisiList.getMaxIdKisi;
  Caption:=Format('%s %s ',[idEnbuyukKisi.adi,idEnbuyukKisi.soyadi]);
end;

Örneği(XE2) burdan indirebilirsiniz.

17 Mart 2013 Pazar

Native Xml Karakter Problemi Çözümü

Unit NativeXmlHelper;

interface

uses NativeXml;

type

  TXmlNodeHelper = class helper for TXmlNode
  public
    function getValue: string;
  end;

implementation

{ TXmlNodeHelper }
function TXmlNodeHelper.getValue: string;
begin
  Result := UTF8ToString(Self.ValueAsUnicodeString);
end;

end.
Kullanımı :
var
 nNode: TXmlNode;
 nNode.getValue;
Diğerlerinde de işe yarayabilir belki.
Kolay gelsin.

23 Şubat 2013 Cumartesi

UnManaged Exports

Bazen .net assembly'lerini delphi de kullanmak ihtiyacı hasıl olur veya kullanmak zorunda kalırız.
Delphiden  LoadLibrary apisi ile yüklediğimiz .net assembly'leri içindeki methodları çağırabilmemiz(adresleyebilmek) için assembly içindeki methodların export edilmiş olması gerekir.

assembly içindeki methodları export etmek için;

İyi çalışmalar...

20 Ocak 2013 Pazar

RTTI ile sadece o sınıfa ait methodları listelemek

RTTI ile bir sınıfa ait methodları listeliyebilmemin mümkünatı aşikardır.Lakin şöyle bir durum var.
Eğer methodlarını listelemek istediğiniz sınıf , birden çok sınıftan kalıtıla gelinerek yazılmış bir sınıfsa;

Alt sınıfın miras alınabilen tüm methodları,üst sınıfında methoduymuş gibi oluyor.
GetMethods ile o sınıfa ait methodları, listelemek istediğinizde taban sınıftaki methodlarda listeleniyor.
Haliyle bu methodlar ne ayak diyorsunuz.

Eğer kendi eklediğiniz methodları listelemek istiyorsanız....
var
  cntx: TRttiContext;
  typ: TRttiType;
  method: TRttiMethod;
begin
  cntx := TRttiContext.Create;
  typ := cntx.GetType(TServerMethods1);
  lBoxMethods.Items.Clear;
  for method in typ.GetMethods do
  begin
    if method.Parent.Name = typ.Name then
      lBoxMethods.Items.Add(method.Name);
  end;
end;

Nerden icab ettiğine gelince...
procedure TForm1.lBoxMethodsDblClick(Sender: TObject);
var
  methodName, url: string;
begin
  methodName := TListBox(Sender).Items[TListBox(Sender).ItemIndex];

  StartServer;
  url := Format('http://localhost:%s/datasnap/rest/TServerMethods1/' +
    methodName, [EditPort.Text]);
  ShellExecute(0, nil, PChar(url), nil, nil, SW_SHOWNOACTIVATE);
end;

İyi çalışmalar...

25 Eylül 2012 Salı

Datasnap rest sunucusu ile iletişim(Delphi Client)

Forma bir tane TIdHttp nesnesi ekliyorsunuz.Daha sonra;
 //http://host:port/datasnap/rest/[ClassName]/[MethodName]/[ParamValue
 Memo1.Text:=IdHTTP1.Get('http://localhost:8080/datasnap/rest/TServerMethods1/GetPersonList');

Datasnap REST iletişim protokolü ve detaylar hakkında buradan malümat edinebilirsiniz

1 Eylül 2012 Cumartesi

Dependency Injection Giriş

{
  Inversion of Control
  Dependency injection at Delphi for Loose Coupling
  İsmail KOCACAN 2012
}

unit uMain;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs;

type
  IDocument = interface
    // GUID oluşturmak için kısayol : Ctrl+Shift+G
    ['{44E273D9-3C17-4821-B791-31CDDCE6C6D8}']
    procedure Parse;
  end;

  TBaseDocument = class abstract(TInterfacedObject)
  private
    FPath: string;
    FContent: string;
  public
    property Path: string read FPath write FPath;
    property Content: string read FContent write FContent;
  end;

  TXMLDocument = class(TBaseDocument, IDocument)
  public
    procedure Parse;
  end;

  TJSONDocument = class(TBaseDocument, IDocument)
  public
    procedure Parse;
  end;

  TDocumentParser = class
  private
    FDocument: IDocument;
  public
    constructor Create(aDocument: IDocument);
    procedure DoParse;
  end;

  TfrmMain = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

{ TXMLDocument }
procedure TXMLDocument.Parse;
begin
  OutputDebugString('XML parseleme işlemi yapıldı.');
end;

{ TJSONDocument }
procedure TJSONDocument.Parse;
begin
  OutputDebugString('JSON parseleme işlemi yapıldı.');
end;

{ TDocumentParser }
constructor TDocumentParser.Create(aDocument: IDocument);
begin
  FDocument := aDocument;
end;

procedure TDocumentParser.DoParse;
begin
  FDocument.Parse;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
var
  axmldocument: TXMLDocument;
  ajsondocument: TJSONDocument;
  aParser: TDocumentParser;
begin
  axmldocument := TXMLDocument.Create;
  aParser := TDocumentParser.Create(axmldocument);
  aParser.DoParse;

  ajsondocument := TJSONDocument.Create;
  aParser := TDocumentParser.Create(ajsondocument);
  aParser.DoParse;
end;

end.
TBaseDocument taban sınıfını, "IInterface" arayüzünün üyelerini implemente etmiş,
ve "TObject" sınıfın tüm özelliklerini bünyesinde barındıran "TInterfacedObject" sınıfından kalıtıyoruz.

"TBaseDocument" sınıfını "TInterfacedObject" sınıfından kalıtmamızın sebebi ise ;
Üst sınıflarda(TXMLDocument,TJSONDocument) "IDocument" arayüzünü implemente ederken
"IInterface" arayüzünün fonksiyonlarını da bizden implemente ettirmek istemesidir.
"TBaseDocument" sınıfından kalıtarak oluşturduğumuz her yeni sınıfın,"IInterface" arayüzünün methodlarını da, implemente ettirmek kullanışlı bir yöntem değil...

O Sebeble ; TBaseDocument taban sınıfımızı türetebileceğimiz,"IIInterface" arayüzünün fonksiyonlarını implemente etmiş,başka bir sınıf lazım.Bu ihtiyacı karşılayacak olan sınıfı kendimizde yazabilirdik fakat,
hali hazırda var olan "TInterfacedObject" ihtiyacımızı karşılıyor. "TBaseDocument" sınıfını TInterfacedObject sınıfından kalıtmamızın sebebi de budur.

"TXMLDocument" ve "TJSONDocument" sınıflarını "TBaseDocument" sınıfından türetiyoruz.
Ve "IDocument" arayüzünün methodlarını implemente ettiriyoruz.
Nitekim her yeni TBilmemneDocument nesnesinin Parse procedure'sinin gövdesinde farklı işlemler olacaktır.

Dependency injection prensibinin "Setter" procedure'ler ve Constructor methodlar ile uygulanadığını okumuştum.Tabiki bence sadece bunlarla sınırlı değil injection olayı...
Biz nesneleri dış dünyaya,o nesnenin üyeleri ile açtığımız için,bence sadece set edilebilen, bir property üzerinden de injection yapılabilir diye düşünüyorum...
injection kısmı ile daha kullanışlı yazım şekilleri aklımda var ama onlar bir daha ki sefere...

Dependency injection prensibin uygulandığı kısım ise "TDocumentParser" sınıfının yapılandırıcısında.
constructor Create(aDocument: IDocument);
Bu şekilde yazmakla derleyiciye şunu demiş oluyoruz.IDocument arayüzünde veya IDocument arayüzünü implemente etmiş bir sınıf, parametre olarak geçilebilir.Bu şekilde yazmak tipden bağımsız olduğu için, daha esnek bir kodlama oluyor.Nasıl yani? Esnek derken ?

Yani IDocument arayüzünü implemente etmiş,TBaseDocument sınıfında türeyen ,yeni yeni sınıflar yazıp,
TDocumentParser sınıfın yapılandırıcında hiçbir değişiklik yapmadan,bu yeni sınıfları parametre olarak geçebiliriz.Bu tipden bağımsız,soyutlama yöntemi ile yazım tarzıydı.


Şimdi birde soyutlamadan yazdığımızı düşünecek olursak;
constructor Create(aDocument: TXMLDocument); overload;
constructor Create(aDocument: TJSONDocument); overload;
Her yeni yazdığımız üst sınıfda(misal TBilmemneDocument) Parse methodunu çağırabilmemiz için,
constructor Create(aDocument: TBilmemneDocument); overload; gibi değişikliği veya başka bir yazım tarzını, "TDocumentParser" sınıfında yapmamız gerekecekti.


Rtti ve Dependency Injection ile ilgili aklımda bir senaryo da var.
Bir daha ki sefere...


Kaynak kodları(XE2) buradan indirebilirsiniz

Kaynaklar : 
http://martinfowler.com/articles/injection.html
http://www.kodcu.com/2011/04/inversion-of-control-ioc-nedir/

28 Ağustos 2012 Salı

Firemonkey DbExpress MySQL Bağlantısı

Merhaba ;
Delphi FireMonkey ile dbexpress kullanarak MySQL Server bağlantısını anlatmaya çalışacağım. Formun üzerine dbexpress sekmesinden TSQLConnection ve TSQLQuery nesneleri bırakılır.Daha sonra;

Windows için ;

TSQLConnection nesnesinin ConnectionName MySQL seçmeniz yeterli. MySQL Client ‘library dosyasının versionu 5.1.59 olmalıdır.

MacOSX için;

  SQLConnection1.ConnectionName:='MySQLConnection';
  SQLConnection1.DriverName:='MySQL';
  SQLConnection1.LibraryName:='libsqlmys.dylib';
  SQLConnection1.VendorLib:='libmysqlclient.dylib';
  SQLConnection1.LoginPrompt:=False;
  SQLConnection1.Open;
 http://dev.mysql.com/downloads/mysql/5.1.html  adresine girilir,ilgili sürüm indirilir.

Ben ; Version 5.1.63 ve Platform MAC OS X seçerek;

Mac OS X ver. 10.6 (x86, 32-bit), Compressed TAR Archive dosyasını indirdim.

mysql-5.1.63-osx10.6-x86lib klasörü içersindeki ;

libmysqlclient.dylib (Kısayol dosyasını) ve libmysqlclient.16.dylib dosyasını da MAC OS işletim sistemine publish etmeniz gerekiyor.

MySQL Client Library dosyalarını MAC’a yüklemek için;

Aktif firemonkey projenizin Project -> Deployment menüsüne tıklanır

Gelen ekrandan Add Feature Files butonuna tıklanır.

Gelen ekrandan DbExpress MySQL Driver düğümü altındaki OSX32 düğümü altındaki libsqlmys.dylib dosyası seçilir.

Add Files butonu tıklanarak MySQL sitesinden indirdiğimiz mysql-5.1.63-osx10.6-x86lib dizini içersindeki libmysqlclient.dylib ve libmysqlclient.16.dylib dosyalarıda deployment listesine eklenir.

Connect To Remote Machine butonuna tıklanarak bağlandıktan sonra Deploy butonu ile MAC’a MySQL Client Library dosyalarını yükleyebilirsiniz.

Demo Görünüm : 




Denemelerimi MAC OS X Lion 10.7.4 üzerinde yaptım.
Vakit buldukça yaptığım denemeleri sizlerle paylaşmaya çalışıcam.

TcxExtLookupComboBox ve TcxGrid Seçili Satıra Erişmek

Diyelim ki TcxExtLookupComboBox ve TcxGrid ile lookUp gerçekleştiriyorsunuz.
Ve yapmak istediğiniz açılan listeden seçili satırın değerlerine erişmek.
procedure TForm1.cxExtLookupComboBox1KeyDown(Sender: TObject; var Key: Word;Shift: TShiftState);
var
 RecordIndex: Integer;
 AValue: Variant;
begin
  if Key = VK_RETURN then 
  begin
    RecordIndex := (Sender as TcxDBExtLookupComboBox).Properties.View.DataController.GetFocusedRecordIndex;
   //Seçili satırın 0 ile ilk kolonunda değere erişiliyor
   AValue := (Sender as TcxDBExtLookupComboBox).Properties.View.ViewData.DataController.GetValue(RecordIndex, 0);
   ShowMessage(AValue);
 end;
end;

TProc Kullanımı

{İsmail Kocacan}
unit uTasks;

interface
uses
  SysUtils,
  ExtCtrls,
  Forms;

type
  TTask = class
  private
    FProc: TProc;
    FTimer: TTimer;

    procedure OnTimer(Sender: TObject);
  public
    procedure Run(AProc: TProc);
    procedure Start;
    procedure Stop;
    constructor Create;
    destructor Destroy;
  end;

implementation

{ TTask }
constructor TTask.Create;
begin
  inherited;
  FTimer := TTimer.Create(Application);
  FTimer.Interval := 50000;
end;

procedure TTask.Run(AProc: TProc);
begin
  FProc := AProc;
  FTimer.OnTimer := OnTimer;
end;

procedure TTask.Start;
begin
  FTimer.Enabled := True;
end;

procedure TTask.Stop;
begin
  FTimer.Enabled := false;
end;

destructor TTask.Destroy;
begin
  FTimer.Free;
end;

procedure TTask.OnTimer(Sender: TObject);
begin
  FProc();
end;

end.
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;

implementation

uses uTasks;
{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  ATask: TTask;
begin
  ATask:=TTask.Create;
  ATask.Run(
    procedure
     begin
       Memo1.Lines.Add('Çalışıyor...');
     end
  );
  ATask.Start;
end;

end.

Bir prosedüre referans gösteren tip diye açıklayabiliriz.
Benim örnektede Run Prosedürünün parametresine bir procedüre gövdesi geçtik.
Detaylar SysUtils.pas 3903 den itibaren başlıyor.
İyi Çalışmalar

TPredicate(T) Kullanımı

{
 İsmail Kocacan
 TPredicate Kullanımı
}
unit Unit1;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  StdCtrls;

type

  TStringListHelper = class helper for TStringList
    function Where(Condition: TPredicate): TStringList;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
 
  public

  end;

var
  Form1: TForm1;




implementation

{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);
var
  AList: TStringList;
begin
  AList := TStringList.Create;
  AList.Add('Ali');
  AList.Add('Veli');
  AList.Add('Deli');
  AList.Add('İsmail');

  Memo1.Lines.AddStrings(
      AList.Where(
              function(Arg: Integer): Boolean
                  begin
                    Result := Arg > 3;
                  end
      )
  );

  AList.Free;
end;


{ TStringListHelper }
function TStringListHelper.Where(Condition: TPredicate): TStringList;
var
  I: Integer;
  NewList: TStringList;
begin
  NewList := TStringList.Create;
  for I := 0 to Self.Count - 1 do
  begin
    if Condition(Length(Self[I])) then
      NewList.Add(Self[I]);
  end;
  Result := NewList;
end;

end.

Yukarıdaki örnekte TStringList'e Where isminde bir extension(uzanım) fonksiyon yazdık.
Bu fonksiyon TStringList de bulunan,karakter sayısı 3 den büyük elemanları, yeni bir liste olarak geriye döndürür.

C# Predicate Kullanımı Bknz : http://msdn.microsoft.com/en-us/library/bfcke1bz.aspx
C# Extension Method Kullanımı Bknz : http://msdn.microsoft.com/en-us/library/bb383977.aspx

Rtti Ortak Method Çağırma(Poliformizm İçerir)

{ By İsmail Kocacan }
unit uReflection;
interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,

  System.Rtti,
  System.TypInfo;

type
  TForm1 = class(TForm)
    Button1: TButton;
    lBox: TListBox;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TTabanClass = class abstract(TObject)
  protected
    procedure Guncelle; virtual; abstract;
  end;

  TGumrukYonetmeligi = class(TTabanClass)
  public
    procedure Guncelle; override;
  end;

  TTebligler = class(TTabanClass)
  public
    procedure Guncelle; override;
  end;

var
  Form1: TForm1;

  atebligler: TTebligler;
  agumrukyonetmeligi: TGumrukYonetmeligi;

implementation

{$R *.dfm}

{ TTebligler }
procedure TTebligler.Guncelle;
begin
  ShowMessage('Tebliğler Güncellendi...');
end;

{ TGumrukYonetmeligi }
procedure TGumrukYonetmeligi.Guncelle;
begin
  ShowMessage('Gümrük Yönetmeliği Güncellendi...');
end;



procedure TForm1.Button1Click(Sender: TObject);
var
  r: TRttiContext;
  rtype: TRttiType;
  obj: TObject;
begin
  r := TRttiContext.Create;
  for rtype in r.GetTypes do
  begin
    if (rtype.TypeKind = tkClass) and rtype.IsInstance and
      rtype.AsInstance.MetaclassType.InheritsFrom(TTabanClass) and
      (rtype.Name <> TTabanClass.ClassName) then
    begin
      lBox.Items.Add(rtype.Name);
      obj := rtype.AsInstance.MetaclassType.Create;
      TTabanClass(obj).Guncelle;
    end;
  end;
  r.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  atebligler         := TTebligler.Create;
  agumrukyonetmeligi := TGumrukYonetmeligi.Create;
end;
end.

Yukarıda TTabanClass isminde soyut bir sınıf ve bu sınıfa ait Guncelle isminde soyut bir prosedür var.
TGumrukYonetmeligi ve TTebligler sınıfları TTabanClass isimli sınıftan türetilmiş ve Güncelle prosedürlerinin gövdeleri yapılandırılmıştır.
FormCreate eventinde de TTabanClass sınıfından türetilen TTebligler ve TGumrukYonetmeligi sınıflarından birer tane nesne referansı oluşturulmuştur.

Şimdi benim yapmak istediğim de şuydu.
"TTabanClass" sınıfından türeyen tüm sınıfların,nesne referanslarının, ortak methodu olan "Guncelle" prosedürünü çalıştırmaktı.
Yaptım oldu  :)

Düzenlemeler : DelphiTürkiye Forum

27 Ağustos 2012 Pazartesi

Delphi XE3 Helpers

XE3 ile gelen yeniliklerden biride daha önce record'lara ve class'lara yazdığımız uzanım fonksiyonlarını artık temel veritiplerine de yazabiliyoruz.Bu güzel bir durum.Bak şimdi oldu :)
unit Unit1;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs;

type
  TDoubleHelper = record helper for Double
    function KareKokAl: Double;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
 sayi:Double;
begin
 sayi:=100;
 ShowMessage(FloatToStr(sayi.KareKokAl));
end;

{ TDoubleHelper }
function TDoubleHelper.KareKokAl: Double;
begin
  Result:=Sqrt(Self);
end;

end.