ในการพัฒนาโปรแกรมเนี่ยเรามักจะมีการตั้ง 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 เนี่ยมันไม่ใช่คอมไพล์เลอร์ มันไม่จับโค๊ดที่ผิดนะครับ อย่างโค๊ดข้างบนเองเอาจริง ๆ ก็คอมไพล์ไม่ผ่านนะขอบอก