WebAssembly เมื่อการพัฒนาเว็บเข้าสู่อีกโลกนึง

ในยุคที่มองไปทางไหนก็มีแต่การพัฒนา Software ด้วย Web Technology ไปหมดทุกทางและยิ่งเป็น Modern Web ที่มี JavaScript เป็นหัวใจสำคัญแล้วนั้น หลายๆปัญหาทางด้าน Performance ก็จะไปติดคอขวดอยู่ที่ Layer ของ JavaScript Engine ดังนั้นทางออกหนึ่งที่ทาง Community นั้นได้มองเห็นก็คือการต่อตรงกับ OS ไปเลยแทนที่จะต้องผ่านการแปล JavaScript ซึ่งนี่ก็คือหนึ่งในที่มาของ WebAssembly

WebAssembly คือการนำ Low Level Code ที่เขียนด้วย C/C++ (และอาจจะมีภาษาอื่นๆในอนาคตเพราะตอนนี้ก็มีภาษา Rust ที่ใช้ได้ด้วยเหมือนกัน) มาทำการรันภายใต้ Web Browser เพื่อให้ได้ Performance  สูงที่สุดรวมทั้งยังสามารถติดต่อกับ JavaScript ได้ด้วย ซึ่งหมายความว่ามันไม่ได้เป็นภาษาใหม่ที่จะมาแทน JavaScript แต่มันคือสิ่งที่จะช่วยยกระดับการพัฒนาเวปมากกว่าครับ และที่สำคัญโปรเจคนี้พัฒนาด้วยเจ้าใหญ่แห่งวงการทั้งนั้น เช่น Mozilla Microsoft Google Apple

เนื่องจากว่า WebAssembly นั้นค่อนข้าง Low Level มาก(แค่มีคำว่า Assembly ก็แลดูน่ากลัวแล้ว) ดังนั้นนักพัฒนาที่สนใจในเรื่องนี้ก็จำเป็นต้องรู้จักสิ่งเหล่านี้ก่อนครับ

  • Source Code แน่นอนว่าคือไฟล์ที่พวกเราเขียน Code ลงไปโดยจะแบ่งเป็น 2 ส่วนคือฝั่ง C/C++ กับฝั่ง JavaScript ครับ
  • Compiler เป็นตัวแปลงภาษา C/C++ ไปเป็น Bytes Code หรือ Machine Code ซึ่งภาษาที่ต้องใช้ Compiler นั้นจะเป็นภาษาที่มี Performance สูงกว่าภาษาพวก Script เพราะไม่ต้องแปลง Code ตอน เวลา Runtime
  • Bytes Code เป็นภาษาระดับกลางที่ทาง WebAssembly Runtime นั้นเข้าใจและทำงานติดต่อกับ OS ตรงๆ
  • Interoperability เป็นการทำงานเชื่อมกันระหว่างระบบหนึ่งกับอีกระบบหนึ่งเช่น การเรียกให้ Module ที่เขียนด้วย C++ ทำงานด้วย Code ของภาษา JavaScript หรือด้านสลับกัน

หลักการพัฒนาเว็บด้วย WebAssembly ก็คือ

  1. Developer เขียนโปรแกรมด้วย C/C++
  2. Developer ทำการ Compile C/C++ ที่เขียนไว้ให้เป็น WebAssembly Bytes Code (ไฟล์ .wasm) เพื่อให้พร้อมที่จะรันได้ใน Browser
  3. Developer ต้องเขียน JavaScript ให้ทำการโหลด .wasm ไฟล์มาไว้ใน Memory เพื่อให้ WebAssembly Runtime ที่อยู่บน Browser นั้นทำการแปลง Bytes Code ให้เป็น JavaScript Object
  4. Developer เรียกใช้งาน function ที่เขียนด้วย C/C++ ผ่าน JavaScript

ทีนี้เรามาลองดูแบบ Step by Step กัน

ก่อนอื่นเลยผมมี Code ที่เขียนด้วย C ง่ายๆคือ function การบวกเลข ช่ือไฟล์ demo.c

Screen Shot 2560-03-05 at 7.04.29 PM.png

สิ่งที่ต้องทำขั้นต่อไปคือ Compile Code ชุดนี้ให้เป็น .wasm ซึ่งเครื่องมือที่ใช้ค่อนข้างซับซ้อนสำหรับมือใหม่(อย่างผม) ซึ่งใช้ emcc (Emscriptenซึ่งเป็น Open Source โปรเจคซึ่งติดตั้งจาก ที่นี่

$emcc demo.c -Os -s WASM=1 -s SIDE_MODULE=1 -o democ.wasm

ด้วย command ข้างบนนี้เราก็จะได้ democ.wasm ซึ่งเป็นไฟล์ Bytes Code ที่ใช้งานได้กับ Browser ที่สนับสนุนแล้ว เช่น Google Chrome 57+ ขึ้นไป

หน้าตาของไฟล์ .wasm ก็เป็นแบบนี้ครับ!! อ่านไม่ออกแน่นอน เพราะเป็น Bytes Code 🙂

Screen Shot 2560-03-05 at 7.03.33 PM.png

ถัดมาเราก็มาเขียน JavaScript ให้การโหลดไฟล์ democ.wasm ซึ่งผมใช้ fetch API ครับและหลังจากนั้นเป็นการเรียกใช้งาน function

Screen Shot 2560-03-05 at 7.06.57 PM.png

จาก Code นี้ในฝั่งของ JavaScript นั้นผมขอแบ่งออกเป็น 4 ส่วนครับ

  1. โหลดไฟล์ .wasm ด้วย fetch API
  2. ทำการประกาศ imports Object เพื่อให้ตัว WenAssembly ทำงานได้ถูกต้อง เช่นการกำหนดขนาดของ  Memory
  3. เป็นการสร้าง Module Object จาก .wasm
  4. เป็น Code ที่ใช้เรียก function “add” ที่เขียนด้วยภาษา C (ในการ Compile นั้นทาง Compiler ได้เติม _ เพื่อเป็น prefix ให้กับชื่อ function ทำให้เราต้องเรียก function นี้ด้วย _add แทน)

ทีนี้ลองมารันดูครับด้วย localhost:8080/demo.html แต่ก่อนอื่นเราต้องมี Browser ที่รองรับก่อน

เตรียม Browser ให้พร้อม

ผมเลือกใช้ Google Chrome Canary Build (ซึ่งเป็น Google Chrome เวอร์ชั่นทดสอบ) ซึ่งตอนนี้เป็นเวอร์ชั่น 59 ครับ เราต้องเปิด flag สำหรับ WebAssembly ด้วยเพราะตอนนี้ยังเป็นแค่ฟีเจอร์สำหรับการทดสอบอยู่ด้วย

chrome://flags/#enable-webassembly

Screen Shot 2560-03-05 at 8.34.54 PM.png

ผลของการทำงานก็จะเห็นได้ว่า function _add นั้นทำการบวกค่าได้อย่างถูกต้อง

Screen Shot 2560-03-05 at 7.11.48 PM.png

สรุป

ก่อนอื่นต้องบอกว่า WebAssembly นั้นเป็นของที่ยังถือว่าใหม่และยังไม่นิ่งเพราะถึงแม้ว่าจะเปิดตัวมาตั้งแต่ปี 2015 แต่จนถึงวันนี้ก็ยังอยู่ในสถานะที่กำลังออกแบบกันอยู่ ซึ่งจากการที่ผมได้ลองอ่านและเล่นๆดูก็พบว่าบทความบนอินเตอร์เน็ตนั้นน้อยมาก กว่าผมจะพอปะติดปะต่อภาพรวมของการทำงานของมันจนมาถึงลองเขียน Code ตัวอย่างดูก็ใช้เวลาอยู่พักนึง แต่ผมว่าเมื่อถึงเวลาที่มันออกสู่สาธราณะเป็น Official Release ฟีเจอร์นี้ก็น่าจะถูกจับตามองอีกครั้งกับการใช้งานหนักๆ เช่น เกมหรืองานคำนวณ รวมทั้งงานที่เน้นกราฟฟิคหนักๆ

Advertisements

TypeScript มาเพิ่มคุณภาพของ Code ด้วย TsLint

Screen Shot 2560-02-04 at 10.23.56 AM.png

Linter นั้นคือเครื่องมือที่ช่วยให้ตรวจสอบลักษณะการเขียน Code ว่าเหมาะสมกับ Best Practices ที่ทาง Community ต่างๆแนะนำหรือไม่ ซึ่งจุดประสงค์หลักเพื่อช่วยให้ Source Code ของทั้งโปรเจคนั้นมีความ

  • อ่านง่าย และไปในทิศทางเดียวกัน
  • ดูแล แก้ไขง่าย
  • แก้ปัญหา Error แบบพื้นฐาน

โดยสำหรับ TypeScript นั้นgเครื่องมือนี้มีชื่อว่า TsLint

 การติดตั้งก็ใช้ npm เจ้าเก่า

$ npm install tslint typescript

จากนั้นก็ทำการสร้าง tslint.json ซึ่งจะเป็นไฟล์หลักที่ใช้ในการ Config ค่าต่างๆของ TsLint

$tslint –init

ลองเปิดดูไฟล์ tslint.json เราก็จะเห็นว่าทางค่าปกติที่ติดตั้งมานั้นมีกฏอะไรบ้าง ซึ่งใน version 4.4.2 ที่ผมใช้นั้นมันซัพพอร์ท JavaScript ด้วย

Screen Shot 2560-02-04 at 9.33.30 AM.png

กับอีกส่วนนึงคือ Node สำหรับ TypeScript ที่ใช้ชื่อ attribute ว่า rules เฉยๆ

Screen Shot 2560-02-04 at 9.36.27 AM.png

ทดสอบกับ Code ตัวอย่าง

ที่นี้มาลองเล่นกันกับ Code ตัวอย่างแบบง่ายๆกับ main.ts

// main.ts
class HERO{
 private name = 'HERO'
 public attack()
 {
 console.log(`${this.name} : do attack`);
 }
}

var player = new HERO();
player.attack();

หลังจาก compiled .ts ออกมาเป็น .js ก็จะออกมาเป็น

// main.js
var HERO = (function () {
 function HERO() {
 this.name = 'HERO';
 }
 HERO.prototype.attack = function () {
 console.log(this.name + " : do attack");
 };
 return HERO;
}());
var player = new HERO();
player.attack();

ลองรันดูก็ได้ผลออกมาตามนี้ครับ ซึ่งก็ถูกต้องดีหมายความว่า Code เรารันได้ดี 🙂

Screen Shot 2560-02-04 at 9.50.44 AM.png

ทีนี้ลองมารัน TsLint เพื่อดูว่า Code ของเรา 10 บรรทัดเนี่ยมันเขียนมาโอเคมั้ยกัน

$ tslint -c tslint.json *.ts -t stylish

ผลออกมากโดนไป 5 เรื่องครับ tslint จะรายงานมาว่าเราเขียนไม่ดียังไง ที่บรรทัดไหน ตัวอักษรที่เท่าไหร่

screen-shot-2560-02-04-at-9-54-26-am

เรื่องที่เราโดนรายงานครั้งนี้คือ

  1. ใช้ var แทนที่จะเป็น let หรือ const เพราะในโลกของ TypeScript เราแนะนำกันแบบนี้
  2. ลืมเว้นวรรค!!
  3. ใส่วงเล็บปีกกาเปิดผิดที่!@!#
  4. ตัวแปร String เราดันใช้ single quote ” แทนที่จะเป็น dobule quotes “” (ตามที่กำหนดไว้ใน tslint.json)
  5. ลืมเครื่องหมาย semi colon

จะเห็นว่าสิ่งที่มันรายงานผลมานั้นก็ดูดีมีประโยชน์ครับ(มั้ย?)

ที่นี้เรามาดู Code หลังจากแก้ไขไปแล้วดีกว่าเนอะ

Screen Shot 2560-02-04 at 10.15.16 AM.png

จากรูปด้านซ้ายและขวาที่เหมือนเล่นเกมจับผิด จะเห็นได้ว่าการทำ Linter นั้นช่วยให้ Code ของเรานั้นดูเป็นระเบียบมากขึ้น ยิ่งในกรณีที่ในโปรเจคมี Developer หลายๆคน อาจะใช้ Linter นั้นจะช่วยให้ Code ที่ถูกเขียนมานั้นเป็นไปในแนวทางเดียวกันมากขึ้น

การทำให้ Software นั้น Maintain ได้ง่ายและมีคุณภาพ เป็นเกียรติ (Duty) ของ Developer นะครับ 🙂

node-ffi เรียก functions ใน dll ด้วย JavaScript กันตรงๆ

จากความเดิมตอนที่แล้วผมเคยเขียนเรื่องการสร้าง Node.js Module ด้วย C++ มาทีนึงแล้ว แต่ถึงอย่างนั้นถ้าเรา
– มี C++ Library อยู่แล้วล่ะ
– หรืออยากทำให้ Node สามารถเรียกใช้งานพวก System API โดยที่ไม่อยากศึกษาการเขียน Node Module แบบ Native
ผมก็ไปเจอว่ามี Community ที่ได้พัฒนา Framework มาตัวนึงซึ่งวันนี้เราจะมาพูดถึงกัน นั่นก็คือ Node-ffi ซึ่งย่อมาจาก Foreign Function Interface

Node-ffi นั้นเป็น Module ที่ทำหน้าที่เป็นตัวกลางในการทำการติดต่อระหว่าง JavaScript Code ไปที่ C++ Library โดยที่เราไม่ต้องเขียน Native Module เอง ประมาณรูป Diagram ด้านล่างครับ

ffi.png

เรามาลองดูการใช้งานกันดีกว่าครับ

ก่อนอื่นเรามี C++ Library ที่เป็น DLL ที่ต้องการจะเรียก ซึ่งผมลองเขียนมาแบบง่ายๆคือ dll ที่มี function ในการ ดึงค่า Version ของ OS

extern "C" DLLIMPORT int getVersion()
{
 DWORD dwVer = ::GetVersion();
 DWORD majorVer = (DWORD)(LOBYTE(LOWORD(dwVer)));
 return majorVer;
};

ซึ่ง API ::GetVersion นั้นเป็นของ Windows SDK ครับ ซึ่งสำหรับฝั่ง C++ ผมได้ทำการ  Compile ออกมาเป็น dll ชื่อ dev.dll

ทีนี้มาลองดูส่วนที่สำคัญของเราก็คือฝั่ง JavaScript

var ffi = require('ffi');

var devModule = ffi.Library('dev', {
 'getVersion': [ 'int', [] ]
});
console.log('Windows version : ' + devModule.getVersion());

จาก Code นี้ก็มี 3 ส่วนหลักๆครับ

  1. require module ‘ffi’
  2. ประกาศการทำ function binding
    ffi.Library('dev', {
      'getVersion': [ 'int', [] ]

    2.1 ทำการ binding กับไฟล์ library ชื่อ dev.dll
    2.2 ทำการ binding กับ function ชื่อ getVersion โดยมี ‘int’ เป็น Type ของ Return Value
    2.3 Function getVersion นี้ไม่มี parameter เลยใส่เป็น []

  3. เรียกใช้ ffi ด้วย  devModule.getVersion()

ผลของการรันก็เป็นดังนี้ครับคือได้ค่า ’10’ มาจาก getVersion

ffi_run.png

นอกจากนี้เรายังสามารถประกาศการทำ binding ทีละหลายๆ function ก็ได้เหมือนกันครับ(ขออนุญาติไม่โชว์ Code ฝั่ง C++ ที่เขียนเพิ่มเพื่อดึงค่า Minor,Build Version) เช่น

var devModule = ffi.Library('dev', {
 'getVersion': [ 'int', [] ] ,
 'getMinorVersion' : [ 'int', [] ],
 'getBuildVersion' : [ 'int', [] ]
});
console.log('Windows version : ' + devModule.getVersion());
console.log('Minor version : ' + devModule.getMinorVersion());
console.log('Build version : ' + devModule.getBuildVersion());

ผลการรันก็ดูดีครับและตรงกับ System Info จริงๆนะ 🙂

ffi_run2.png

สรุป : node-ffi น่าจะเป็นอีกหนึ่งทางเลือกสำหรับคนที่เขียน Node.Js ด้วย Context บางอย่างที่ต้องติดต่อกับ System หรือ OS ระดับ Low Level (เหมือนผม..) โดยที่เรานั้นไม่ต้องมาการเรียนรู้การเขียน Node Module แบบ Native ที่อาจจะมีความซับซ้อนกว่าทั้งในแง่การทำ Mapping พวก Data Types รวมถึงการมานั่งศึกษา V8 API  ซึ่งผมคิดว่า node-ffi อาจจะมีผลกระทบกับ Performance ในการ call function บ้าง แต่ผมก็ยังไม่ได้ลองวัด ทั้งนี้ทั้งนั้นใครมีข้อมูลเพิ่มก็แชร์กันได้ครับ

ทำให้ App บน Windows เป็นสีขาว-ดำ

เนื่องจากว่าผมได้พยายามลองทำให้ Application บน Windows นั้นเปลี่ยนเป็นสีโทนขาว-ดำ เลยอยากจะแชร์สิ่งที่ได้ลองทำนะครับ (ถึงแม้ว่าผลของการ Implement นั้นยังไม่ถูกใจเท่าที่ควร)

ก่อนอื่นเลยผมขอเรียก app นี้ว่า Mourning App และเนื่องจากว่าต้องการทำสำหรับ Windows App ดังนั้นภาษาที่ผมใช้คือ C++ และ Win32 API ซึ่งตัว API ที่ใช้หลักๆคือ Magnification API ซึ่งเป็น Windows API ที่ปกติเอาไว้ทำพวก Tool ที่เป็นแว่นขยายต่างๆ

หลักการของ Mourning App ที่ผมทำมี 4 ขั้นตอน คือ

  1. หา Process ที่เราสนใจจะทำการ apply การทำภาพขาวดำก่อน

    API ที่ผมใช้ส่วนนี้คือ

    ::EnumProcesses ซึ่งทำให้เราได้จำนวน Process ที่กำลังรันอยู่ทั้งหมดมาพร้อมทั้ง Process ID ซึ่งเราจะใช้เอาไว้หาชื่อ Process ที่เราสนใจได้

  2. หา Window Handle (hwnd) ที่มันทำการแสดงผลอยู่ (visible) ของ Process นั้นๆ

    Code หลักๆที่ใช้ก็คือ  ::EnumWindows
    ซึ่งจากนั้นใน Callback ของ EnumWindowsProc นั้นจะได้ hwnd มาเรื่อยๆซึ่งผมจำการเช็ค

    if (!::IsWindow(hwnd) && !::IsWindowVisible(hwnd))
    return true;

  3. Render ทับตำแหน่ง application จริงๆด้วย Magnifier window พร้อมกับแปลงให้เป็นสีแบบ Gray Scale

    ส่วนนี้เป็นส่วนสำคัญที่สุด ซึ่งใช้ Magnification API โดยการสร้าง Window ด้วย Style : MS_CLIPAROUNDCURSOR

    m_hwndMag = CreateWindow(WC_MAGNIFIER, TEXT(“MagnifierWindow”),
    WS_CHILD | MS_CLIPAROUNDCURSOR | WS_VISIBLE

  4. ทำการ Refresh รูปบ่อยๆเพื่อให้ภาพเนียนที่สุดเวลา Application มีการ update หน้าจอ

    ส่วนนี้ไม่มีอะไรมากก็คือการตั้ง Timer เพื่อทำการ Refresh UI ครับแต่เราจะ Refresh เฉพาะ window ไม่ใช่ทั้งหน้าจอ Desktop

ตัวอย่างที่ได้ครับ

vscode
vscode in grayscale
chrome.png
Chrome เป็นสีเทา แต่ Edge ยังเป็นสีปกติ
ffx2.png
เกม FFX-2 ก็ทำให้ขาว-ดำ ได้นะครับ

ที่นี้มาดูผลที่ยังไม่น่าพอใจใน Implement ครั้งนี้ครับ

  1. ตัว render ภาพขาว-ดำนั้นเป็น Top Window ซึ่งทำให้ถ้ามี App ที่เปิดทับมันก็จะเป็นสีขาว-ดำด้วย
  2. ช้า เนื่องจากการ refresh นั้นบ่อยๆมีผลกับ Performance ทำให้ภาพมี delay บ้าง
  3. CPU ขึ้นตลอดเวลา 5% เนื่องจากต้อง query Process ตลอดเวลาในการเช็คว่ามี Window ของ Process ที่เราจะทำการ render ขาว-ดำ เปิดมาใหม่รึเปล่า

สุดท้ายนี้ ใครสนใจอยากดูตัวอย่างก็ Github เลยครับผม https://github.com/anurocha/mourning-win-app

เข้าสู่ C++ ยุคใหม่กับ Module System สำหรับ C++ 17/20

ไม่ยอมน้อยหน้าภาษา Modern เพราะหนึ่งใน Feature ที่มีการพูดถึงกันมากของมาตรฐานใหม่ของ C++ 17 ก็คือเรื่อง Module System (Link) ซึ่งเป็นแนวคิดทำให้ Library เป็น Module แบบจริงจัง โดยที่ทางกลุ่ม C++ Working Group ได้นำเสนอมานานแล้ว และมีแผนที่จะเอาเข้ามาเป็นส่วนหนึ่งของ C++ 17 แต่เหมือนว่าไม่น่าจะทัน C++ 17 แล้วครับ สรุปแล้วตอนนี้ Feature นี้อยู่ในขั้น Technical Specification ซึ่งน่าจะออกจริงใน C++ 20 (เลขข้างหลังเป็นเลขที่ เช่น 2017 หรือ 2020)

เป้าหมายของ Module System คือทำให้ C++ นั้นทำการ Compile ได้เร็วขึ้นและทำให้ Code นั้นมีการจัดการระดับ Component ได้ง่ายขึ้น เพราะปัจจุบัน #include นั้นใช้งานปนไปหมดทั้ง Headers, Inlines รวมทั้งบางทีมี Implemenation กันเลยทีเดียว

Microsoft นั้นได้ออก Preview สำหรับการทดลอง Module ใน Visual Studio 2015 Update 1+ มาลองดูกันเลยดีกว่า

ก่อนอื่นเลยครับ เนื่องจากว่า Visual Studio ในส่วนของ IDE นั้นยังไม่ Support ดังนั้นเราจะต้องทำการ Compile กับ Build ผ่าน Command Line นะครับ

สร้าง Module

ในการสร้าง Module นั้นให้เราสร้างไฟล์ใหม่มา ซึ่งโดยทั่วไปจะนิยมใช้ไฟล์ .ixx (ผมเดาว่ามาจาก Interface++)
เรามาสร้าง Module เอาไว้ Convert ค่าง่ายๆกัน

ไฟล์ converter.ixx

module converter;
export int CtoB(int x)
{
    return x + 543;
}

จาก code นี้จะเป็นการประกาศ Module ชื่อ converter ซึ่งมี function ‘CtoB’ ที่ทำการแปลง ค.ศ. ไป เป็น พ.ศ. ที่จะ export ออกไปให้ Code อื่นใช้

จากนั้นเราต้อง compile ด้วย commandline แบบนี้

cl /c /experimental:module converter.ixx

สิ่งที่ได้ก็คือไฟล์ converter.ifc กับ converter.obj

converter.png

เรียกใช้ Module

ที่นี้มาในส่วนของ Code หลักที่มี main entry point กันครับ ผมตั้งชื่อไฟล์ว่า main.cpp

ไฟล์ main.cpp

#include <iostream>
import converter;
int main()
{
 int year;
 std::cout << "Enter Year : ";
 std::cin >> year; 

 int bc = CtoB(year);
 std::cout << "Thai year is " << bc << std::endl;
 return 0;
}

Code ในส่วนนี้มีการเรียกใช้ Module ชื่อ ‘converter’ ด้วย keyword ที่ชื่อ import จากนั้นก็จะเรียกใช้ function ที่ชื่อ CtoB() ได้ทันทีเลยครับ

แน่นอนว่าเราต้อง compile ด้วย command line เช่นเคยโดยต้องระบุ converter.ifc เป็น parameter ของ module ที่จะเรียกใช้

cl /c /experimental:module /module:reference converter.ifc main.cpp

เมื่อรันเสร็จก็จะได้ main.obj มาครับ

main.png

ที่นี้จะเป็นว่าไม่มี .exe ให้เรา….สิ่งที่ต้องทำคือการสร้าง .exe ด้วย

link *.obj /OUT:main.exe

link.png

ทดสอบ

ลองรัน main.exe ที่ได้มาครับ ทำงานถูกต้องนะ 🙂

out.png

ลองด้วย Class

จากตัวอย่างข้างบนนั้นเป็นการสร้าง Module ด้วยการ export แค่ function แต่ถ้าเราอยากที่จะ  export Class ล่ะ เราก็ทำได้ครับ เช่น สร้าง Class  converter โดยมี method 2 ตัวให้เรียกใช้

module converter;
export namespace converterUtil
{
 class converter
 {
 public:
 int CtoB(int year)
 {
 return year + 543;
 }

 int CelsiusToFahrenheit(int c)
 {
 return (c * 1.8) + 32;
 }
 };
}

มาดูที่ main.cpp เราก็ทำการ import มาใช้และสร้าง Object ได้เลย เช่นใน ‘convt’ ตาม Code ด้านล่างนี้

#include <iostream>
import converter;
int main()
{
 converterUtil::converter convt;

 int year;
 std::cout << "Enter Year : ";
 std::cin >> year; 

 std::cout << "Thai year is " << convt.CtoB(year) << std::endl;

 int cel;
 std::cout << "Enter Temp in Celsius : ";
 std::cin >> cel; 
 std::cout << "Temp in Fahrenheit is " << convt.CelsiusToFahrenheit(cel) << std::endl;

 return 0;
}

ผลที่ได้ก็ Work ดีครับ!!

outex.png

สรุปแล้ว C++ Module น่าจะช่วยให้ C++ Developer ออกแบบและทำงานกับ Code ได้เป็นระเบียบเรียบร้อยขึ้น

และอีกอย่างคือจริงๆเท่าที่ผมลองในการสร้าง Module ถ้ามันมีใช้ header ตัวอื่นผมก็ยังทำให้มันเวิร์คไม่ได้นะ จึงต้องยกตัวอย่างแบบง่ายๆแบบนี้ครับ ซึ่งหวังว่าตัวจริงออกมาคงไม่วุ่นวายมากนะ

ณ. ตอนนี้เราก็มาเฝ้ารอ C++ 20 กันเลยกัย Module System!!

ปล. ดู Code ได้ที่ https://github.com/anurocha/cpp_module เลยครับผมอัพไว้ละ

Node.js – LoopBack กับการต่อ Database

ความเดิมตอนแรกสุดเลยนั้น ผมใช้ datasources เป็น memory เพราะว่าใช้ในการ Development ที่นี้เนื่องจากว่าผมอยากใช้ database เป็น NoSQL ผมก็เลยลองหาวิธีการดู ซึ่งก็ไม่ผิดหวังครับ LoopBack นั้นจัดการให้เราได้อย่างง่ายๆเช่นเคย

บทความก่อนหน้าเพื่อความเข้าใจพื้นฐานมากขึ้น

Node.js เขียน REST API ด้วย LoopBack
Node.js – LoopBack มาต่อกันที่การเพิ่ม REST API กับ Query กัน

ที่นี้เปิดดูไฟล์ datasources.json กันก่อนครับ จะเห็นว่าเป็นการ define ว่าใช้ memory

{
  "db": {
    "name": "db",
    "connector": "memory"
  }
}

แก้ datasources.json
ผมก็แค่เปลี่ยน configuration ในนี้ให้เป็น  ‘mongodb’ ซึ่งผมทดสอบกับ Azure DocumentDB service นะครับ ไม่ได้ตั้ง mongoDB เอง

nosql.png

ที่นี้เราลองมารัน LoopBack กันอีกรอบนึงครับ ปรากฎว่ารันไม่ผ่าน เพราะ LoopBack ไม่รู้จัก configuration นี้

no-connector.png

ติดตั้ง Connector
สิ่งที่เราต้องทำคือการ install mongodb connector ครับ ด้วย npm command ข้างบน

npm install loopback-connector-mongodb

เมื่อ install เสร็จก็จะสามารถรันได้แล้วครับ เช่นเดิมคือ port เบอร์ 3000

has-connector.png

ทดสอบ
ที่นี้เราลอง POST เพื่อใส่ข้อมูลกัน ผม POST  – anurocha กับ destiny ไปครับ

post-db.png

ที่นี้ลอง Query ดูใน Azure Dashboard ก็จะเห็นว่าลองใช้ Query Explore บน Azure ดูก็ได้ค่าที่ POST ไปจาก localhost ที่เราต่อกับ mongoDB บน Azure ถูกต้อง

azure-db.png

จะเห็นได้ว่า LoopBack นั้นช่วยให้เรา Connect ไปที่ Database ได้โดยที่ผมยังไม่ได้เขียน Code หรือ SQL เลยโดยที่ datasource ที่ LoopBack นั้นมี Connector ก็ดูได้ตามนี้ครับ (Database connectors) ซึ่งคร่าวๆก็มีครบหมดครับสำหรับตัวยอดนิยม MongoDB, MySQL, Oracle, PostgreSQL, Redis, SQL Server, SQL Lite และอื่นๆ

เป็นไงครับ POWER FUL มากๆ…

Node.js – LoopBack มาต่อกันที่การเพิ่ม REST API กับ Query กัน

หลังจากที่ผมพูดถึงความสามารถเบื้องต้นของ LoopBack ไปแล้วนั้นใน Node.js เขียน REST API ด้วย LoopBack ซึ่งจะเห็นได้ว่าเราสามารถสร้าง REST API ได้ง่ายมาก แต่ทีนี้ถ้าเราต้องการที่เพิ่ม API ล่ะจะทำได้ยังไง

เรากลับมาดูที่ไฟล์ user.js กันก่อนครับ ซึ่งเป็นไฟล์ว่างๆ

'use strict';

module.exports = function(User) {

};

สิ่งที่ผมอยากจะได้ก็คือถ้าเราต้องการ API เพื่อที่จะเอาค่าของ User 4 คนที่ได้คะแนนสูงสุดจะต้องทำยังไงบ้าง
สิ่งที่เราต้องทำคือการเพิ่ม remoteMethod (ซึ่งทาง LoopBack Framework จะทำการ Route มาเข้า Method ของเราครับ

'use strict';

module.exports = function(User) {
User.getTopUsers = function(cb){
cb(null, "getTopUsers Response");
};

User.remoteMethod (
'getTopUsers',
{
http: {path: '/gettopusers', verb: 'get'},
returns: {arg: 'users', type: 'string'}
}
);
};

จะเห็นว่า Code User.remoteMethod นั้นทำการ define ว่าถ้ามี HTTP Request ที่เป็น GET เข้ามาที่ Path /gettopusers นั้นจะทำการเรียก Method User.getTopUsers นั่นเอง

และใน Method ที่ชื่อ User.getTopUsers นั้นตอนนี้ผมลองให้ส่งค่ากลับไปว่า getTopUsers Response ที่นี้ลองรันดู LoopBack เพื่อเป็น Server ดูครับ

ลองเช็คกับ API Explorer ก็จะเห็นว่า API ที่เราเพิ่งเพิ่มเข้าไปมาแล้ว

new-api.png

เนื่องจากเป็น GET ผมลองเรียกผ่าน Browser ครับก็ได้ Response มาแบบนี้ ตามที่เขียนไว้

new-api-response.png

ที่นี้จาก API ที่ผมต้องการนั้น ผมอยากได้ User ที่คะแนนสูงสุด ซึ่งทาง LoopBack นั้นก็มี Build-in API ให้เราใช้ในการ Query ข้อมูลจาก Model ได้ครับ (Querying data) เช่น

'use strict';

module.exports = function(User) {
User.getTopUsers = function(cb){
User.find({
order: 'score DESC',
limit : 4
}, (err, instances)=&gt;{
cb(null, instances);
});
};

User.remoteMethod (
'getTopUsers',
{
http: {path: '/gettopusers', verb: 'get'},
returns: {arg: 'users', type: 'string'}
}
);
};

ซึ่งจาก Code ข้างบนนี้จะเป็นการใช้ User.find ซึ่งเป็นการ Query หา Model โดยมี Filter ว่า หา ‘score’ ที่มีค่าสูงสุด 4 entries แรก

ที่นี้ลองทดสอบกันอีกทีครับ

ผมลองใส่ ข้อมูลไปแบบนี้ตาม Order

[
{
“name”: “alpha”,
“score”: 70,
“id”: 1
},
{
“name”: “beta”,
“score”: 45,
“id”: 2
},
{
“name”: “catto”,
“score”: 39,
“id”: 3
},
{
“name”: “delta”,
“score”: 86,
“id”: 4
},
{
“name”: “sigma”,
“score”: 34,
“id”: 5
},
{
“name”: “fanta”,
“score”: 12,
“id”: 6
}
]

ทีนี้ลองเรียกผ่าน API /gettopusers ดูครับ

result.png

เราก็จะได้ Response ที่ทำการ Order มาแล้วตาม score โดยที่ delta(80), alpha(70), beta(45), catto(39) ตามลำดับที่เราได้ทำ Query ไว้อย่างง่ายดาย

มาถึงตรงนี้เราก็จะได้เห็นความ Powerful ของ LoopBack ไปอีกหนึ่งเรื่องนะครับ 🙂

powered-by-lb-med