[Programming Technique] - #3 Thunk - Return

เดี๋ยวจะหาว่า เอามะพร้าวห้าวมาขายสวน มีแต่เทคนิคบ้านๆ แหะ

คราวนี้เอาให้มันส์หน่อย หัวข้อนี้มาจาก Game Programming Gems เล่ม 2 นะ ใครมีครบชุดยกมือหน่อย

จริงๆ เทคนิคนี้เรียกว่า stack winding เป็นการเปลี่ยน ค่าใน stack เพื่อให้มันกระโดดไปกระโดดมาตามที่เราต้องการแทน

แต่โปรแกรมเมอร์ในไมโครซอฟท์ได้ใช้ code พวกนี้มากๆ ในระดับล่างๆ แล้ว ก็เรียกมันว่า thunk คือ วิ่งๆ อยู่ ตูม ออกจาก function ไป

ก่อนอื่นดูตัวอย่าง code ข้างล่างก่อน ว่าเจ้า Thunk - Return มันจะใช้ยังไง

ตัวอย่าง code สมมุติ เรามี sequence ของ code อยู่ใน block หนึ่ง ซึ่ง ก่อนจะทำงาน code ที่อยู่ตรงกลางได้ เราต้องทำการ setup อะไรบางอย่าง และ หลังจากทำงาน เสร็จก็ต้อง clean up มัน



  Setup_1();
  Setup_2();
  Do_3();
  CleanUp_2();
  CleanUp_1();
  
เจ้า Thunk - Return นี้ จะกรุ๊ปการทำงาน ของ code ชุดนี้ ออกเป็น function ต่างๆ



  void Set_1()
  {
   Setup_1();
   THUNK
   CleanUp_1();
   RETURN
  }
  
  void Set_2()
  {
   Setup_2();
   THUNK
   CleanUp_2();
   RETURN
  }
  
  void CodeSequence()
  {
   Setup_1();
   Setup_2();
   CALL Do_3();
  }
  
ซึ่งประโยชน์ของมันคือ เราจะเขียน ชุดคำสั่งในการ setup และ clean up ไว้ด้วยกันได้ ทำให้ ไม่หลงลืม และ code เราก็เป็นระเบียบมากขึ้นด้วย

คราวนี้มาดู implementation ของจริงกัน



  #define THUNK __asm pop edx __asm call edx
  #define RETURN __asm ret
  #define CALL __asm jmp
  
  __declspec(naked) void Set_1()
  {
   printf("Setup_1\n");
  
   THUNK
  
   printf("Cleanup_1\n");
  
   RETURN
  }
  
  __declspec(naked) void Set_2()
  {
   printf("Setup_2\n");
  
   THUNK
  
   printf("Cleanup_2\n");
  
   RETURN
  }
  
  void Do_3()
  {
   printf("Do_3\n");
  }
  
  __declspec(naked) void Setup()
  {
   Set_1();
   Set_2();
  
   CALL Do_3
  }
  
  int _tmain(int argc, _TCHAR* argv[])
  {
   Setup();
  
   return 0;
  }
  
code Thunk - Return นี้ผมได้ทดสอบกับ VC++ 2005 express edition เป็น win32 console application ก็ใช้ได้ คิดว่า ถ้า compile แบบ win32 GUI application ก็ใช้ได้เหมือนกัน

จุดสำคัญของ function เหล่านี้ คือ
- Thunk - Return function (Set_1, Set_2) ต้อง declare เป็น void foo(void) แบบ global เท่านั้น (อย่าลืม ว่าเราแทนที่ code sequence ด้วย function)
- ตรง statement สุดท้าย (CALL Do_3) ยังไงก็ต้องมีการ jump นะครับ เพราะตอน thunk Set2 stack มันเปลี่ยนไปแล้ว
- สุดท้ายเจ้า void foo(void) จะต้องระบุ compiler spec. เป็น naked เพื่อไม่ให้มันใส่ prolog/epilog code และการระบุ compiler spec. ต้องระบุที่ function definition (ตามตัวอย่างข้างบน แต่ถ้าคุณไประบุที่ declaration เช่น __declspec(naked) void Setup(); อย่างนี้ไม่ถูกนะครับ )

ตัวอย่าง prolog code ที่ได้จาก VC++ 2005 express edition จะเห็นงานประจำของพวกมันคือ push register กับพวก return address



  00411B10 push ebp
  00411B11 mov ebp,esp
  00411B13 sub esp,0C0h
  00411B19 push ebx
  00411B1A push esi
  00411B1B push edi
  00411B1C lea edi,[ebp-0C0h]
  00411B22 mov ecx,30h
  00411B27 mov eax,0CCCCCCCCh
  00411B2C rep stos dword ptr es:[edi]
  
จบไปอีกอันนะครับ ถ้าคราวหน้า ผมยังมีแรง จะเอา stack winding ที่ใช้กับ recursive function มาให้ดูกัน ซึ่งมันจะช่วยเพิ่มความเร็วของ recursive code ได้ไม่น้อยหล่ะ

ขอบคุณที่ทนอ่านถึงบรรทัดนี้เน้อ




 

Create Date : 30 ตุลาคม 2550   
Last Update : 30 ตุลาคม 2550 1:10:13 น.   
Counter : 286 Pageviews.  

[Programming Technique] - #2 Eliminate nested 'if'

มาต่อตอน 2 ด้วยการกำจัดประโยค if ที่ซ้อนกันหลายๆ ชั้น ซึ่งเราจะได้ code ที่อ่านได้ง่ายขึ้น หลายคนคงเคยไล่ if block ที่ซ้อนกันหลายๆ ชั้น คงนึกออก

ตัวอย่าง


  while(1)
  {
   if(i == 0)
   {
   DoSomething_1();
   
   if(j > 3)
   {
   if(k == 5)
   DoSomething_2();
   else
   DoSomething_3();
   }
   else
   DoSomething_4();
   
   DoSomething_5();
   }
  }
   
อันนี้ตัวอย่างง่ายๆ ถ้า code ยาวๆ แต่ละ block สัก 4-5 บรรทัด ก็จะเริ่มตาลายได้
ทีนี้เรามาเอา nested if block ออกกัน โดย

- inverse logic รวมกับการใช้ end block keyword ต่างๆ อันได้แก่ break continue
- combine logic เข้าด้วยกัน


  while(1)
  {
   if(i != 0)
   continue;
   
   DoSomething_1();
   
   bool IsDoSomething_2 = j > 3 && k == 5;
   bool IsDoSomething_3 = j > 3 && k != 5;
   
   if(IsDoSomething_2)
   DoSomething_2();
   else
   if(IsDoSomething_3)
   DoSomething_3();
   else
   DoSomething_4();
   
   DoSomething_5();
  }
   
จบอีกตอน หวังว่า code ของท่านๆ จะสละสลวยเข้าตากรรมการยิ่งขึ้น




 

Create Date : 29 ตุลาคม 2550   
Last Update : 30 ตุลาคม 2550 11:26:10 น.   
Counter : 230 Pageviews.  

[Programming Technique] - #1 Pseudo programming

หัวข้อนี้กะว่า จะรวมเทคนิคเล็กๆ น้อยๆ ในการเขียนโปรแกรมนะ

บางอย่าง บางคนอาจจะรู้อยู่แล้ว ที่มาก็จากที่เคยอ่านหนังสือดังๆ เช่น Code complete หรือ จากประสบการณ์ของตัวเอง

ก็เริ่มเลยดีกว่า

Pseudo programming คือเทคนิคที่จะใช้ในการ code procedure หนึ่งๆ (ถึงภาพรวมของโปรแกรมของเราจะเป็น OOP ก็ตาม) เป็นเทคนิคที่ทำให้ code ของเรา

- มี Objective คือ รู้ว่า code จะทำอะไร ไม่ใช่บอกว่า ทำอย่างไร
- มี Order คือ ช่วยเราลำดับการทำงานอของ procedure ก่อนที่จะ code ลงไปจริงๆ
- เพิ่ม Readability ทั้งตัวเราและคนอื่นจะอ่าน code ได้ง่ายขึ้น

ตัวอย่าง - สมมุติเขียน Server โดยใช้ TCP socket โดยเราต้องการให้โปรแกรมเราเปิด thread ใหม่ ทุกครั้งที่มี connection จาก client เข้ามา

เริ่มทำ pseudo code



  // Create a server socket
  // Bind the socket
  // Listen to a given port
  // Wait an incoming connection
  // Create a worker thread
  // Start a worker thread
   
จาก pseudo ข้างบน เราจะเห็นลำดับการทำงานเพื่อแก้ปัญหาของเรา

ทำความเข้าใจกันก่อน
1. Code ต่อไปนี้ ไม่มีใน library ไหนครับ อย่าถามหาเลย ผมมั่วมาเอง
2. ผมไม่ชอบ exception

ทีนี้เราก็เริ่มใส่ code เข้าไป



  // Create a server socket
  cSocketTcp *pServerSocket = cSocketTcp::Factory();
  // Bind the socket
  pServerSocket->Bind();
    
  // Listen to a given port
  pServerSocket->Listen(Port)
    
  // Handling an error on socket established
  if(pServerSocket->IsError())
   return pServerSocket->Error();
    
  // Wait an incoming connection
  cSocketTcp *pClientSocket;
  pClientSocket = pServerSocket->WaitClientConnection();
    
  // Create a worker thread
  cThread *pWorkerThread = cThread::Factory(WorkerThread);
    
  // Start a worker thread
  pWorkerThread->SendParam(*pClientSocket );
  pWorkerThread->Start();
    
จะเห็นว่า ผมได้เพิ่มบรรทัดที่ทำการ handling error เข้าไป ซึ่งก็ไม่ผิดอะไร เพราะตอนแรก ผมก็นึกไม่ถึงเหมือนกัน 555

เป็นไงครับผม สำหรับเทคนิคนี้ ถ้าชอบ ก็ช่วยส่งเสียงหน่อย คราวหน้า (คงมีอีกแน่ เพราะเวลาว่างเยอะนี่ อมยิ้มโดนแบน ) จะหาเรื่องมันส์ๆ มาฝากอีกนะครับ บ๋ายบาย...




 

Create Date : 28 ตุลาคม 2550   
Last Update : 30 ตุลาคม 2550 11:27:47 น.   
Counter : 281 Pageviews.  

[C++] - ค่า PI

สวัสดีทุกท่าน

หลังจากผมมี Internet ใช้ที่บ้าน เหมือนชาวโลกเค้าแล้ว ก็เลยอยากจะ Update blog เน่าๆ นี้หน่อย

วันนี้ขอนำเสนอบทความเกี่ยวกับค่า PI บทความนี้จริงๆ ได้ถูกตีพิมพ์ในเดือนพฤษภาคมปี 2003 ใน "C/C++ User Journal" ที่หายสาบสูญไปแล้ว (ไปรวมกับ Dr. Dobb's Journal) - ขอไว้อาลัย ณ. ที่นี้

ผมจะเขียนมันขึ้นจากความทรงจำน่ะหล่ะ

ค่า PI ใน library ของภาษา C++ นั้นไม่ได้ถูก define ไว้ เพราะฉะนั้น เวลาเราจะใช้เรา ก็ต้องทำการ define มันขึ้นมา


  #define PI 3.141926535897932384626433832795
 
เห็นอะไรไหมเอ่ย คือค่าข้างบนนี้เราพิมพ์ตกไปตัวนึง ที่ถูกต้องคือ


  #define PI 3.1415926535897932384626433832795
 
อันนี้เป็นปัญหาค่า Constant value ในโปรแกรมต่างๆ ของเรา แต่เป็นโชคดีของ ค่า PI ที่เรามันวิธีแก้ปัญหานี้อย่างฉลาดๆ

เนื่องจาก Intel math co-processor ได้รวมคำสั่ง ที่สามารถนำ ค่า PI จาก processor มา push ลง stack ได้ คือ fldpi ดูตัวอย่างนะครับ


  #include "stdio.h"
  
  class cPI
  {
   public:
   operator double () const
   {
   double Pi;
   _asm
   {
   fldpi; // Push the constant pi to the x87 stack
   fst Pi; // Store to Pi
   }
   return Pi;
   }
  };
  
  static cPI PI;
  
  int main()
  {
   double x = PI;
  
   printf("Pi = %f\n", x);
  
   return 0;
  }
  
วิธีการนี้คือ เราสร้าง class cPI ขึ้นแล้ว overload double operator เพื่อทำ implicit conversion สำหรับตัวแปร double (ข้อดีของวิธีนี้ คือ จะเห็นได้ว่าตอนเรียกใช้ ค่า PI มันจะเหมือนเป็นตัวแปรตัวนึงเลย ไม่ได้อยู่ในรูป function)

จากนั้นใน function เราก็ return ค่า PI ออกมาจาก processor เท่านั้นเอง

จบแล้วครับ หวังว่าจะได้มาเขียนอีกบ่อยๆ เนี่ย เสียค่าเน็ทเดือนตั้งแพง เดี๋ยวใช้ไม่คุ้มหล่ะแย่เลย




 

Create Date : 31 มีนาคม 2550   
Last Update : 30 ตุลาคม 2550 11:20:30 น.   
Counter : 893 Pageviews.  

[C++] - Swap integer without variable

Swap integer without variable

เขียนไว้กันลืม



  void swap(int &a, int &b)
  {
   a^=b^=a^=b;
  }
  





 

Create Date : 03 มีนาคม 2549   
Last Update : 30 ตุลาคม 2550 11:21:33 น.   
Counter : 362 Pageviews.  

1  2  

กะต่อ
Location :
กรุงเทพ Thailand

[Profile ทั้งหมด]

ให้ทิปเจ้าของ Blog [?]
ฝากข้อความหลังไมค์
Rss Feed

ผู้ติดตามบล็อก : 1 คน [?]


ผู้ติดตามบล็อก : 1 คน [?]




เพราะทำงานกับเครื่องจักรเลยคิดว่าตัวเองเป็นศิลปิน ออกจะเรื่อยเปื่อย ไม่งอก ไม่ริเริ่มอะไรบ้าง ก็ดี เพราะถ้าเริ่มได้ ก็ต้องจบให้ได้ ฮว๊าก..
[Add กะต่อ's blog to your web]