clang-format – โปรแกรมจัดฟอร์แมทโค๊ดสวย จาก llvm

ในการพัฒนาโปรแกรมเนี่ยเรามักจะมีการตั้ง coding standard ซึ่งก็จะมีส่วนของรูปแบบโค๊ด ไม่ว่าจะเป็นการย่อหน้า การเว้นวรรค การตัดบรรทัด และอื่น ๆ

ปัญหาก็คือ คนเขียนโค๊ดก็เป็นคน และด้วยความที่เป็นคน คนเขียนโค๊ดก็มักจะไม่สามารถรักษาฟอร์แมทของโค๊ดได้ตลอดเวลา และด้วยกฎที่กำหนดอย่างหลวม ๆ ทำให้คนเขียนโค๊ดสามารถที่จะเลือกว่าจะให้กฎข้อไหน และละเลยกฎข้อไหนไป บางโปรเจคก็ถึงขึ้นว่าไม่สามารถรักษาฟอร์แมทให้เหมือนกันได้ทั้งโปรเจค

ปัญหาอีกข้อคือ ฟอร์แมทของโค๊ดนั้นก็เหมือนกับตัวโค๊ด ตรงที่มีการพัฒนาตลอดเวลา ถ้าเรามีการเปลี่ยนแปลงโค๊ดให้เข้ากับฟอร์แมทใหม่ก็ต้องมีคนไปแก้โค๊ดเก่า ถ้าเป็นโปรเจคที่มีไฟล์เยอะมาก ๆ รับรองได้ว่าไม่มีใครทำแน่ ๆ ทั้งเสียเวลาทั้งน่าเบื่อ

ด้วยปัญหาหลาย ๆ อย่างของการจัดรูปแบบด้วยมือก็เลยมีคนทำโปรแกรมที่คอยจัดฟอร์แมทของไฟล์ให้ โปรแกรมประเภทนี้บางคนก็จะเรียกว่า code format หรือบางคนก็จะเรียกว่า code beautifier ซึ่งใน IDE แพง ๆ ส่วนใหญ่ก็จะมีฟีเจอร์นี้อยู่ในตัวครับ

วันนี้จะแนะนำโปรแกรมตัวนึง ชื่อว่า clang-format ซึ่งเจ้านี่เนี่ยเป็นทูลที่ติดมากับชุดคอมไพล์เลอร์ clang ถ้าใครสนใจอยากลองก็หามาติดตั้งได้ครับ เป็นคอมไพล์เลอร์ฟรี แต่บนวินโดวส์อาจจะติดตั้งลำบากนิดนึง เอาไว้วันหลังจะมาแนะนำวิธีง่าย ๆ ให้ฟัง

สมมติว่าผมมีโค๊ดนี้นะครับ

        #include <iostream>
    using namespace std;

    struct Point{ int x, int y};
enum class Direction {Left, Right, Forward, Backward};

void PrintDirection(const Direction &dir){
  switch(dir){
    case Direction.Left: cout<<"Turn Left"<<endl; break;
    case Direction.Right: cout<<"Turn Right"<<endl; break;
    case Direction.Forward: cout<<"Move Forward"<<endl; break;
    case Direction.Barkward: cout<<"Move Backward"<<endl; break;
    default: throw;
  }
}

int main(int, char **) 
{
    PrintDirection(Direction.Left);

    Point p{50,100};

  for(int i = 0; i< 100; i++) {p.x ++; p.y--;}

  return 0;
}

ดูเละ ๆ แบบนี้แหละ 555 ผมสามารถใช้คำสั่ง clang test.pp เพื่อให้มันฟอร์แมทโค๊ดสวย ๆ โดยเมื่อเราสั่งมันจะพิมพ์ออกมาบนคอนโซลครับ

#include <iostream>
using namespace std;

struct Point {
  int x, int y
};
enum class Direction { Left, Right, Forward, Backward };

void PrintDirection(const Direction &dir) {
  switch (dir) {
  case Direction.Left:
    cout << "Turn Left" << endl;
    break;
  case Direction.Right:
    cout << "Turn Right" << endl;
    break;
  case Direction.Forward:
    cout << "Move Forward" << endl;
    break;
  case Direction.Barkward:
    cout << "Move Backward" << endl;
    break;
  default:
    throw;
  }
}

int main(int, char **) {
  PrintDirection(Direction.Left);

  Point p{50, 100};

  for (int i = 0; i < 100; i++) {
    p.x++;
    p.y--;
  }

  return 0;
}

ดูดีขึ้นไหมครับ ทั้งนี้เราสามารถเพิ่มพารามิเตอร์ -i เข้าไปเพื่อให้มันบันทึกลงไปในไฟล์เลยได้ด้วยครับ

ทีนี้ฟอร์แมทของโค๊ดที่ได้เนี่ยมันจะเป็นตามมาตรฐานของโครงการ llvm แต่เราสามารถเลือกที่จะใช้มาตรฐานอื่น ๆ ได้ (ตัวโปรแกรมมีมา 5 รูปแบบครับ) และเราสามารถสร้างรูปแบบของเราเองได้ด้วยอีกเช่นกัน ตัวอย่างของล่างนี้เป็นฟอร์แมทของ WebKit นะครับ

#include <iostream>
using namespace std;

struct Point {
    int x, int y
};
enum class Direction { Left,
    Right,
    Forward,
    Backward };

void PrintDirection(const Direction& dir)
{
    switch (dir) {
    case Direction.Left:
        cout << "Turn Left" << endl;
        break;
    case Direction.Right:
        cout << "Turn Right" << endl;
        break;
    case Direction.Forward:
        cout << "Move Forward" << endl;
        break;
    case Direction.Barkward:
        cout << "Move Backward" << endl;
        break;
    default:
        throw;
    }
}

int main(int, char**)
{
    PrintDirection(Direction.Left);

    Point p{ 50, 100 };

    for (int i = 0; i < 100; i++) {
        p.x++;
        p.y--;
    }

    return 0;
}

ผมเคยดูวิดีโองาน CppCon (ถ้าจำไม่ผิดนะครับ) ซึ่งเซสชั่นนึงมีผู้จัดการของโครงการ LLVM เป็นผู้บรรยาย เขาเล่าให้ฟังว่าตัวโค๊ดของ LLVM เนี่ยจะถูกฟอร์แมทด้วยทูลตัวนี้ก่อนที่จะถูกคอมมิทเข้าไป (ผมเดาว่าเป็น Trigger ครับ) ดังนั้นทุกไฟล์จะมีลักษณะเดียวกันหมด มีความคงเส้นคงวา และถึงอาจจะไม่ได้สวยงามเหมือนโค๊ดที่ทำด้วยมือ แต่ว่าก็ดูแลรักษาง่ายกว่าและมีอัตราการผิดพลาดน้อยกว่าครับ

ส่วนตัวผมก็สนับสนุนให้มีการตั้ง commit trigger ให้ฟอร์แมทโค๊ดก่อนที่จะบันทึกทุกครั้งเหมือนกัน แต่ส่วนตัวยังไม่ได้ทำครับ ตอนนี้ใช้วิธีใช้ Text Editor (Atom) ไปเรียก clang-format ก่อนบันทึกไฟล์แทน ก็พอทดแทนกันได้ในระดับหนึ่งครับ

ทั้งนี้เรื่องหนึ่งที่ต้องบอกคือเจ้า clang-format เนี่ยมันไม่ใช่คอมไพล์เลอร์ มันไม่จับโค๊ดที่ผิดนะครับ อย่างโค๊ดข้างบนเองเอาจริง ๆ ก็คอมไพล์ไม่ผ่านนะขอบอก

Wutipong Wongsakuldej

Programmer, interested in frontend applications, music and multimedia.

Latest posts by Wutipong Wongsakuldej (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *