<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wutipong Wongsakuldej</title>
    <description>The latest articles on DEV Community by Wutipong Wongsakuldej (@mr_tawan).</description>
    <link>https://dev.to/mr_tawan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1014424%2F6aee137f-9411-43e6-8ffe-2e21ee991b6b.jpeg</url>
      <title>DEV Community: Wutipong Wongsakuldej</title>
      <link>https://dev.to/mr_tawan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mr_tawan"/>
    <language>en</language>
    <item>
      <title>Can't avoid boilerplate? Use template project</title>
      <dc:creator>Wutipong Wongsakuldej</dc:creator>
      <pubDate>Fri, 26 May 2023 14:57:59 +0000</pubDate>
      <link>https://dev.to/mr_tawan/cant-avoid-boilerplate-use-template-project-363g</link>
      <guid>https://dev.to/mr_tawan/cant-avoid-boilerplate-use-template-project-363g</guid>
      <description>&lt;p&gt;พอเราเขียนโค๊ดไปสักพักนึง เราจะเริ่มเขียนโปรเจคจำนวนนึงที่แบบ...มันดูคล้าย ๆ กัน มีโค๊ดที่หน้าตาเหมือน ๆ กัน ใช้ไลบราลีตัวเดียวกัน คือจุดเริ่มต้นมันเหมือนกัน แล้วมันค่อย ๆ โตไปเป็นโปรแกรมที่มีวัตถุประสงค์ที่แตกต่างกันในอนาคต&lt;/p&gt;

&lt;p&gt;เช่นแบบ เวลาเราทำโปรเจคที่เป็น React (สมมติ) เราก็ต้องมา import library เพิ่มทาสก์ใน &lt;code&gt;package.json&lt;/code&gt; ต้องเขียนโค๊ดที่หน้าตาคล้าย ๆ กันจำนวนนึง จนกระทั่งมันรันขึ้นแล้วเราค่อยมาแก้ไขให้มันเป็นแอพลิเคชั่นที่เราต้องการ อะไรแบบนี้ใช่ไหมครับ &lt;/p&gt;

&lt;p&gt;วิธีนึงที่จะมาช่วยคือสร้าง tool ที่ generate project file ทั้งหมด ซึ่งเป็นวิธีที่หลาย ๆ เฟรมเวิร์คใหญ่ ๆ ใช้ (React ก็ด้วย) เรียกว่ารันคอมมานด์แล้ว ใส่คำสั่งนิดหน่อย ก็ได้แอพรันขึ้นมาแล้ว&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app my-app
cd my-app
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่อีกวิธีที่เรียบง่ายกว่านั้นคือ สร้างเทมเพลตสำหรับโปรเจคขึ้นมา เวลาจะสร้างโปรเจคใหม่ก็แค่ก็อปปี้เทมเพลตออกมาเป็นโปรเจคใหม่ แล้วก็แก้ไขโปรเจคนั้นให้ตรงกับความต้องการของเรา&lt;/p&gt;

&lt;h1&gt;
  
  
  Template Project ใน GitHub
&lt;/h1&gt;

&lt;p&gt;ใน Github เองมีฟีเจอร์นี้อยู่ครับ เราสามารถสร้างโปรเจคขึ้นมาแล้วระบุว่าโปรเจคนี้เป็น Template ในหน้า Settings ของโปรเจค&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0SyZPiiC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xiz7vmsh9ov6e0hcd2fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0SyZPiiC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xiz7vmsh9ov6e0hcd2fx.png" alt="ติ๊กถูกที่ช่อง Template repository" width="787" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;แค่นี้โปรเจคนี้ก็จะกลายเป็นเทมเพลตของเราเรียบร้อย&lt;/p&gt;

&lt;p&gt;จากนั้น เมื่อเราต้องการจะสร้างโปรเจคใหม่ เราก็แค่เลือกเทมเพลตใน dropdown ตอนสร้าง repository &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E4bJdAJb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wdnd8pkadcqncbckrgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E4bJdAJb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wdnd8pkadcqncbckrgu.png" alt="เลือกเทมเพลต" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;หรืออีกทางเลือกหนึ่งคือ เปิดหน้า repository ของเทมเพลตที่จะใช้ แล้วกด "Use this template"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6NkLOKAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sgm1yg1nu5lopj6kzy9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6NkLOKAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sgm1yg1nu5lopj6kzy9c.png" alt="Use this template -&amp;gt; Create a new repository" width="398" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อ Repository ถูกสร้างเรียบร้อย เราจะได้ repository ใหม่ที่หน้าตาเหมือนกับตัวเทมเพลตเป๊ะ ๆ โดยที่เราไม่ต้องมานั่งก็อปปี้ไฟล์ไปมา&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nMXguO-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjnrn73vdtnjoc569toc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nMXguO-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjnrn73vdtnjoc569toc.png" alt="โปรเจคใหม่" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n57QYNhE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3t916j3wl3cgprg2y9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n57QYNhE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3t916j3wl3cgprg2y9w.png" alt="เทมเพลต" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;สิ่งที่แตกต่างคือ ตัวหัวโปรเจคจะระบุไว้ว่าสร้างจากเทมเพลตไหน และโปรเจคใหม่ที่สร้างขึ้นมาจะมีสถานะเป็นเหมือนโปรเจคใหม่ที่มี commit แรกจากตัวเทมเพลต ไม่มี branch ไม่มี history ที่ติดมาจากตัวเทมเพลต เรียกได้ว่าเริ่มต้นกันแบบคลีน ๆ ครับ&lt;/p&gt;

&lt;p&gt;ทั้งนี้ ถ้าเกิดว่าตัวเทมเพลตเรามี GitHub Action อยู่ เมื่อเราสร้าง repository ใหม่ เราจะได้ Action ติดมาด้วย เนื่องจากว่าไฟล์ Action ก็เป็นแค่ source file ธรรมดาใน repository เท่านั้นเองครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ieha3N5q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u6w01kv4ykdhhjp1vn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ieha3N5q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u6w01kv4ykdhhjp1vn5.png" alt="GitHub Action" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เรียกได้ว่าพอสร้างเสร็จก็พร้อมใช้งานทันทีเลยล่ะ เริ่มรันได้เลย เราสามารถเริ่มต้นทำงานได้ทันทีโดยไม่ต้องมานั่งเซ็ตอัพอะไรให้วุ่นวายอีกครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nKf-U3P_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yuu8phbjskbwjh6upf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nKf-U3P_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yuu8phbjskbwjh6upf4.png" alt="Image description" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Array ใน C++</title>
      <dc:creator>Wutipong Wongsakuldej</dc:creator>
      <pubDate>Mon, 06 Feb 2023 14:24:52 +0000</pubDate>
      <link>https://dev.to/mr_tawan/array-ain-c-1nmo</link>
      <guid>https://dev.to/mr_tawan/array-ain-c-1nmo</guid>
      <description>&lt;p&gt;ในยุคเริ่มต้น เรื่อง array ของ C++ เนี่ยจริง ๆ เป็นฟีเจอร์ที่ยกมาจากภาษา C ซึ่งเวลาเราประกาศตัวแปรเป็น array เราก็จะเขียนแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;anArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราก็จะได้ array ขนาด 20 ช่อง &lt;/p&gt;

&lt;p&gt;ตรงนี้เมื่อเทียบกับภาษาสมัยใหม่ อย่างพวก C#, Java หรือแม้แต่ JavaScript ความแตกต่างอย่างหนึ่งของ array แบบ C คือ ขนาดของ array จะเป็นส่วนหนึ่งของ type ด้วย&lt;/p&gt;

&lt;p&gt;แล้วก็ ตอนที่ initialize ค่าของ array จะต้องทำตอนที่ประกาศทันที ถ้าประกาศเสร็จแล้วเราอยากเปลี่ยนค่า ก็ต้องเปลี่ยนค่าทีละช่อง ราวกับว่าแต่ละช่องเหมือนเป็นตัวแปรตัวหนึ่ง&lt;/p&gt;

&lt;p&gt;สาเหตุหนึ่งคือ array ใน C++ เป็น value type เมื่อคุณสร้าง array ขึ้นมาแล้ว ตัวภาษาจะกันพื้นที่ส่วนหนึ่งในหน่วยความจำไว้ให้เลย เหมือนมีตัวแปร int หลาย ๆ ตัวติด ๆ กันตรงนั้น ต่างกับภาษาอื่นที่ array มักจะเป็น reference ไปหาพื้นที่ที่อยู่ใน heap แทน&lt;/p&gt;

&lt;p&gt;ดังนั้นเราจะเขียนแบบ (ยกตัวอย่าง java)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;anArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;anArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน C++ ไม่ได้&lt;/p&gt;

&lt;p&gt;จุดที่ยากอีกอย่างคือ ตัวขนาดของ array จะต้องรู้ตั้งแต่ตอนที่คอมไพล์โค๊ดเลย ไม่สามารถที่จะระบุภายหลังได้ (เพราะคอมไพล์เลอร์จะกันพื้นที่ให้ตอนนั้นเลย) &lt;/p&gt;

&lt;p&gt;ตัวอย่างข้างล่างนี้คอมไพล์ไม่ผ่าน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// error: ตอนคอมไพล์โค๊ดไม่รู้ว่า size มีค่าเท่าไหร่&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Array
&lt;/h2&gt;

&lt;p&gt;ในภาษา C เนี่ย เราสามารถใช้ pointer ต่าง array ได้ คือเราเห็นตัวแปรไหนเป็น pointer เนี่ย สามารถใส่ subscript ตามหลังได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;anArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// หรือใช้ new int[10] แทนในภาษา C++&lt;/span&gt;
&lt;span class="n"&gt;anArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แน่นอนว่าถ้าเราไปเข้าถึงพื้นที่ที่เราไม่ได้กันไว้ให้ตัวแปรนั้น มันก็จะพัง เท่านั้นเอง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;line 110:    12 Segmentation fault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่งการใช้งานในลักษณะนี้จะเหมือนกับ array ในภาษาสมัยใหม่อย่างที่ว่าข้างบนครับ คือเราใช้ pointer ที่ชี้ไปหาพื้นที่หน่วยความจำที่เป็น array อีกทีนึง&lt;/p&gt;

&lt;p&gt;ดังนั้น สำหรับคนที่เขียนภาษาอื่นมา ถ้าจะเขียน array บางทีเราอาจจะต้องใช้รูป dynamic array แบบนี้ครับ ไม่ใช่รูป array ปรกติ &lt;/p&gt;

&lt;p&gt;อ้อ อีกอย่างครับ ลืม ... array สามารถแปลงร่างเป็น pointer ได้ครับ (แต่แปลงกลับไม่ได้นะ)&lt;/p&gt;

&lt;h2&gt;
  
  
  ปัญหาของการใช้ array แบบภาษา C
&lt;/h2&gt;

&lt;p&gt;อย่างที่เล่าข้างบน Array ในภาษา C นั้น ขนาดของ array จะเป็นส่วนหนึ่งของ type ด้วย แต่ ... เราไม่สามารถบอกจากตัวแปรนั้น ๆ ได้ว่า array นี้มีขนาดเท่าไหร่&lt;/p&gt;

&lt;p&gt;อันนี้รวมทั้ง array และ dynamic array ที่มาในรูป pointer ด้วย&lt;/p&gt;

&lt;p&gt;คือคนเขียนจะต้องรู้เองว่า array เนี่ยมีขนาดเท่าไหร่ หรือ จะต้องส่งตัวแปรอีกตัวมาบอกว่า array นี้มีขนาดเท่าไหร่&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรืออีกวิธีคือ บังคับว่า ต้องปิดท้ายด้วยค่า 0 เท่านั้น ซึ่งพวกฟังก์ชันที่เป็นชุดของ string ในภาษา C จะใช้วิธีนี้ทั้งหมด (เรียกว่า null-terminated string)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;strlen&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่ง มันกลายเป็นว่า วิธีการใช้ array หรือ dynamic array มันก็เลยวุ่นวายแบบสุด ๆ ไปเลย&lt;/p&gt;

&lt;h2&gt;
  
  
  Array ใน C++ ยุคใหม่
&lt;/h2&gt;

&lt;p&gt;ผ่านมาถึงปี 2007 เราคงคิดว่า เฮ้ย ... มันต้องเปลี่ยนแล้วมั้ย ภาษาอื่นเค้า array ใช้ง่ายมาก ทำไม C++ มันใช้ยากอย่างงี้&lt;/p&gt;

&lt;p&gt;ก็เลยเกิด type ที่ชื่อ &lt;code&gt;std::array&lt;/code&gt; ขึ้นมา&lt;/p&gt;

&lt;p&gt;ผู้ใช้ก็ เฮ ... มีไทป์ใหม่แล้ว มันต้องใช้ง่ายเหมือนภาษาอื่นแน่เลย คำตอบคือ ไม่ใช่ครับ ไอ้ &lt;code&gt;std::array&lt;/code&gt; เนี่ย จริง ๆ แล้วมันคือ wrapper ของไอ้ array แบบ C ธรรมดานี่ล่ะ&lt;/p&gt;

&lt;p&gt;ดังนั้นข้อจำกัดที่ว่า ขนาดของ array เป็นส่วนหนึ่งของ type ก็ยังคงจริงอยู่เช่นเดิม&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;anArray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สิ่งที่ง่ายขึ้นคือ เราสามารถบอกขนาดของ array ได้จากตัวแปรนั้นละ และตัว &lt;code&gt;std::array&lt;/code&gt; นี่ก็มีวิธีการใช้งานเหมือนกับ STL container อื่น ๆ ของ standard library ดังนั้นมันก็จะใช้ง่ายขึ้น มีเคสพิเศษน้อยลง&lt;/p&gt;

&lt;p&gt;ทำ re-assignment ก็ยังได้เลย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ไม่ได้หมายความว่า array ที่มี element type เดียวกันแต่ละคนละขนาด จะ assign กันได้นะครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  อ้าวแล้ว Dynamic Array ล่ะ ?
&lt;/h2&gt;

&lt;p&gt;จริง ๆ Dynamic Array ของ C++ นี่เกิดก่อน &lt;code&gt;std::array&lt;/code&gt; อีก นั่นคือ &lt;code&gt;std::vector&lt;/code&gt; ที่อยู่ในชุด STL นั่นเอง&lt;/p&gt;

&lt;p&gt;หลายคนที่ไม่ได้ศึกษาจริงจัง จะไม่รู้ว่ามันใช้แทนกันได้ คือ &lt;code&gt;std::vector&lt;/code&gt; เนี่ย ไส้ในมันคือ dynamic array และมันการันตีว่า ค่าต่าง ๆ ที่อยู่ข้างในนั้นจะวางอยู่ชิดติดกันเสมอ&lt;/p&gt;

&lt;p&gt;คือที่เราเห็นว่า เฮ้ยเราสามารถแทรกค่าได้ สามารถนำค่าออกได้ สามารถขยายขนาดได้ ฯลฯ เนี่ย มันเกิดจากฟังก์ชันต่าง ๆ เค้า implement ข้างในให้หมดเลย เราเลยไม่ต้องแบบ &lt;code&gt;realloc()&lt;/code&gt; หรือย้ายค่าไปมา ทั้งหมด คลาสนี้ทำให้หมดเลย&lt;/p&gt;

&lt;p&gt;ในทางกลับกัน ถ้าเราใช้ &lt;code&gt;std::vector&lt;/code&gt; เหมือนพวก container อื่น ๆ เช่นพวก linked list ก็อาจจะทำให้ performance หดได้เหมือนกัน&lt;/p&gt;

&lt;p&gt;เวลาใช้ vector ให้คิดว่ามันคือ dynamic array เข้าไว้ครับ &lt;/p&gt;

&lt;p&gt;แน่นอนว่ามันมีความแตกต่างอยู่บ้างครับ อย่าง สมมติเราระบุขนาด &lt;code&gt;std::vector&lt;/code&gt; ไว้แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;anVector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ไอ้ตัว vector เนี่ย แทนที่มันจะกันที่ไว้แค่ 20 มันจะกันเผื่อไว้ประมาณนึง คือเค้าออกแบบมาเพื่อว่าเราอาจจะมีการเพิ่มหรือลด element ใน vector ไว้ได้วย ซึ่งถ้าเราซีเรียสเรื่องการใช้งานหน่วยความจำมาก ๆ การใช้ vector ก็อาจจะไม่เหมาะครับ&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>crypto</category>
      <category>web3</category>
    </item>
    <item>
      <title>Ownership ในภาษา C++</title>
      <dc:creator>Wutipong Wongsakuldej</dc:creator>
      <pubDate>Sun, 05 Feb 2023 11:52:08 +0000</pubDate>
      <link>https://dev.to/mr_tawan/ownership-ainphaasaa-c-7gn</link>
      <guid>https://dev.to/mr_tawan/ownership-ainphaasaa-c-7gn</guid>
      <description>&lt;p&gt;ผมเห็นฟีเจอร์นึงของ Rust ที่มีคนพูดถึงคือ ownership ซึ่งมันเป็นการบอกว่า ตัวแปรไหน ใครเป็นเจ้าของ ใน C++ ก็มีฟีเจอร์นี้เหมือนกัน โดยเราเริ่มพูดคุยกันจริงจังในช่วงการพัฒนา Modern C++ (ก็ก่อน Rust หลายปีอยู่) แต่ว่ามันมาในรูปแบบของไลบราลีครับ ซึ่งก็คงจะไม่แน่นเท่าฝั่ง Rust &lt;/p&gt;

&lt;p&gt;ก่อนจะไปถึงฟีเจอร์ในไลบราลีที่ว่านั่น ผมขอเริ่มจาก reference ก่อน&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;สมมติว่า เรามี struct นึง หน้าตาแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;และเรามีโค๊ดแบบข้างล่างนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;200.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Designated Initializer ฟีเจอร์ใหม่ของ C++20&lt;/span&gt;

&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;คนที่เป็นเจ้าของ float ตัวนี้ก็คือ p ดังนั้นถ้า p ถูกทำลายไป ค่านี้ก็จะหายไปด้วย&lt;/p&gt;

&lt;p&gt;อันนี้คือนิยามของ ownership เลยครับ คือ คนที่เป็นเจ้าของ object ใดก็ตาม พอคนนี้ถูกย้ายไป หรือถูกทำลายไป object นั้นจะตามไปด้วย &lt;/p&gt;

&lt;p&gt;ลองดูตัวอย่างถัดไป สมมติว่าผมมี&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;({.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;200.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error! &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นโค๊ดที่น่าเกลียดน่าดู (ฮา) คือ เนื่องจาก x เป็นค่าที่ refer ไปวัตถุที่ตัวพอยน์เตอร์ p ชี้ไป พอเราไปลบมัน x ก็เลย refer ไปไหนก็ไม่รู้ เพราะว่า x ไม่ได้เป็นเจ้าของตัววัตถุที่มัน refer ไปถึง&lt;/p&gt;

&lt;p&gt;(เอาจริง ๆ เราก็ไม่ควรใช้ new/delete หรอกครับ แต่อันนี้ใช้แค่อธิบายโค๊ดเฉยๆ)&lt;/p&gt;

&lt;p&gt;พูดง่าย ๆ reference มันเป็นแค่ป้ายครับ มันไม่ได้เป็นเจ้าของอะไร ก็เหมือนกับไอ้ NFT กับพวกงานศิลปะนี่แหละ NFT ไม่ได้แสดงความเป็นเจ้าของอะไร&lt;/p&gt;

&lt;p&gt;ดังนั้นพอเราเห็น reference เนี่ย บอกได้เลยว่า มันไม่ได้เป็น owner ของวัตถุที่มัน refer ไป พอ reference หายไป ค่านั้นก็ยังอยู่ และถ้ามันดัน refer ไปหาวัตถุที่มีเจ้าของ และเจ้าของนั้นดันหายไปแล้ว reference นั้นก็จะพาลพังไปด้วย&lt;/p&gt;

&lt;p&gt;ขอนอกเรื่องนิ๊ดเดียว ส่วนตัวคิดว่า reference เนี่ย ถ้าใช้เป็นแค่จุดพักข้อมูลระยะสั้น ๆ เนี่ยดีครับ แต่ถ้าเป็นตัวแปรที่ใช้ยาว ๆ มันสามารถสร้างปัญหาที่หาไม่เจอง่าย ๆ ได้ด้วยนะ (เพราะว่ามันไม่ได้เป็นเจ้าของค่าที่มัน refer ไปไง) อันนี้ระวังนิดนึง&lt;/p&gt;

&lt;h2&gt;
  
  
  Pointer
&lt;/h2&gt;

&lt;p&gt;ต่อจาก reference ก็มาต่อที่ pointer ซึ่ง เมื่อเทียบกับ reference แล้ว อันนี้จะกำกวมมากกว่า คือ ถ้าเราเห็นตัวแปรเป็น reference เราสามารถบอกได้เลยว่า เฮ้ยเราไม่ได้ own ไอ้ค่าที่มันชี้ไปนะ แต่พอเป็น pointer เนี่ย เราบอกไม่ได้ว่าเราเป็นเจ้าของหรือเปล่า&lt;/p&gt;

&lt;p&gt;เพราะมันอาจจะเป็นการรับค่ามาจาก reference operator ก็ได้ หรือมาจาก &lt;code&gt;new&lt;/code&gt; operator ก็ได้เหมือนกัน หรือแม้กระทั่ง &lt;code&gt;new[]&lt;/code&gt; operator หรือจะลามไปเป็น string  ฯลฯ &lt;/p&gt;

&lt;p&gt;ปวดหัวกันมากมาย&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart Pointers
&lt;/h3&gt;

&lt;p&gt;ในก๊วน C++ ก็เลยสร้าง type ใหม่ขึ้นมาสำหรับพวก pointer ที่เป็นเจ้าของวัตถุที่มันชี้ไป จะเรียกว่าใช้แทน &lt;code&gt;new&lt;/code&gt; เลยก็ได้ โดยมีอยู่ 3 type ข้างล่าง (จริง ๆ มี 4 แต่ deprecate ไปหนึ่ง)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;unique_ptr&lt;/code&gt; เป็น pointer ที่มีเจ้าของเพียงหนึ่งเดียวเท่านั้น ไม่สามารถสร้างก๊อปปี้ได้&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shared_ptr&lt;/code&gt; เป็น pointer ที่ทุก ๆ ก๊อปปี้ของ pointer นี้เป็นเจ้าของร่วมกัน&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;weak_ptr&lt;/code&gt; เป็น pointer ที่ไม่เป็นเจ้าของใคร สามารถขอความเป็นเจ้าของชั่วคราวจาก &lt;code&gt;shared_ptr&lt;/code&gt; ได้ &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;unique_ptr&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;คำว่า "เจ้าของ" ในที่นี้นั้น จะผูกอยู่กับ lifetime ของตัว pointer เอง ในกรณีของ &lt;code&gt;unique_ptr&lt;/code&gt; นั้น เมื่อตัว pointer ถูกทำลาย ตัววัตถุที่มันชี้ไปก็จะถูกทำลายตามไปด้วย อย่างเช่นตัวอย่าง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// สิ้น scop ตัว p ถูกทำลาย ค่าที่ p ชี้ไปก็จะถูกทำลายไปด้วย&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ลักษรณะเฉพาะอันนึงที่ทำให้มัน unique คือ คุณไม่สามารถสร้างสำเนาของมันได้ วิธีเดียวที่จะสร้างสำเนาได้คือใช้ &lt;code&gt;get()&lt;/code&gt; เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่งคงไม่ต้องให้บอกว่า หายนะกำลังรอคุณอยู่ ....&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;free(): double free detected in tcache 2
timeout: the monitored command dumped core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;shared_ptr&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;สำหรับกรณีของ &lt;code&gt;shared_ptr&lt;/code&gt; นั้น ตัว pointer จะถือ reference counter ร่วมกันอยู่ ทุกครั้งที่มีการสร้างสำเนาของ pointer เจ้า counter นี้ก็จะเพิ่มขึ้นด้วย และเมื่อสำเนาของ pointer นี้ถูกทำลาย counter ตัวนี้ก็จะลดลง &lt;/p&gt;

&lt;p&gt;เมื่อ counter มีค่าเป็น 0 ตัวค่าที่ pointer ชี้ไปก็จะถูกทำลาย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// counter = 1&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//counter = 2&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// สิ้น scope ใน p2 ถูกทำลาย counter = 1 &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// สิ้น scope นอก p1 ถูกทำลาย counter = 0 และ ค่าที่ชี้ไปก็ถูกทำลายด้วย&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;weak_ptr&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;และสุดท้าย คือ &lt;code&gt;weak_ptr&lt;/code&gt; อันนี้จะงงนิดนึง เป็นตัวที่ใช้คู่กับ &lt;code&gt;shared_ptr&lt;/code&gt; เราไม่สามารถใช้มันเดี่ยว ๆ ได้ และไม่สามารถใช้มันตรง ๆ ได้ด้วย&lt;/p&gt;

&lt;p&gt;คืออารมณ์ประมาณนี้ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;weak_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// lock ค่า wp เพื่อป้องกันไม่ให้ค่าที่ตัวมันชี้ไปถูกทำลายโดย sp &lt;/span&gt;
                          &lt;span class="c1"&gt;// จริง ๆ คือมันก็สร้าง copy ของ sp แล้วคืนค่ากลับมาให้ใช้นี่ล่ะ&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;*&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// เขียนค่าทับลงไป ทำให้ค่าเดิมที่ sp เคยชี้ไปหายไปด้วย&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// wp ล๊อคไม่ได้ เพราะค่าที่มันเคยชี้ไปถูกทำลายไปแล้ว&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;*&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="k"&gt;else&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"failed to lock."&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ถึงจะล็อคไม่ได้ โปรแกรมก็ยังไม่พัง ก็ยังหายใจกันต่อ&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200
failed to lock.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผมคิดว่า &lt;code&gt;weak_ptr&lt;/code&gt; นี่ เวลาใช้ต้องคิดนิดนึง แอบใช้ยากอยู่ครับ แต่มันมี use-case แหละ เมื่อเทียบกับ &lt;code&gt;shared_ptr&lt;/code&gt; ซึ่งทำงานใกล้เคียงกับพวก reference ในภาษาอื่น ๆ แล้ว &lt;code&gt;weak_ptr&lt;/code&gt; นี่ต้องคิดนิดนึงเวลาใช้เลยล่ะครับ&lt;/p&gt;

&lt;h4&gt;
  
  
  วิธีใช้ล่ะ?
&lt;/h4&gt;

&lt;p&gt;ส่วนตัวผมใช้ &lt;code&gt;unique_ptr&lt;/code&gt; สำหรับวัตถุหนึ่งเดียวที่มีในโปรแกรม พวกวัตถุที่แทนค่า device หรือพวก global instance ต่าง ๆ &lt;/p&gt;

&lt;p&gt;ส่วน &lt;code&gt;shared_ptr&lt;/code&gt; จะเป็นพวก pointer ทั่ว ๆ ไป พวก instance ต่าง ๆ จะใช้ตัวนี้ครับ&lt;/p&gt;

&lt;p&gt;ส่วน &lt;code&gt;weak_ptr&lt;/code&gt; นี่ ผมว่าเหมาะกับการเอาไปใช้ใน function ที่รับค่าเป็น &lt;code&gt;shared_ptr&lt;/code&gt; reference อย่างน้อยก็เช็คได้ว่าค่าที่มันชี้ไปยังใช้งานได้นะ แล้วก็เหมาะกับพวก instance ที่ไม่ได้แคร์ว่า ไอ้ pointer ที่มันถืออยู่จะใช้ได้หรือไม่ได้ ถ้าใช้ไม่ได้ก็ข้ามไป อะไรแบบนี้ครับ (อธิบายยากนิดนึง)&lt;/p&gt;

&lt;h3&gt;
  
  
  แล้วเคสอื่นๆ ล่ะ
&lt;/h3&gt;

&lt;p&gt;เอากรณีที่ผมนึกออกนะครับ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใช้ pointer เป็น string &amp;lt;&amp;lt; ใช้ &lt;code&gt;std::string&lt;/code&gt; แทน&lt;/li&gt;
&lt;li&gt;ใช้ pointer เป็น dynamic array &amp;lt;&amp;lt; ใช้ &lt;code&gt;std::vector&lt;/code&gt; แทน อันนี้หลายคนไม่รู้ จริง ๆ &lt;code&gt;std::vector&lt;/code&gt; ก็คือ wrapper ของ dynamic array นี่ล่ะ ใช้ง่ายกว่าด้วย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แล้วพอหมดพวกเคสพิเศษพวกนี้ ที่เหลือคือกรณีที่ pointer มันชี้ไปหาวัตถุที่มีเจ้าของอยู่แล้ว อันนี้คือเราปล่อยได้เลย ไม่ต้องไปยุ่งกับมันครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;ตัว ownership เป็นคอนเซปท์ที่ เอาจริง ๆ ณ.จุดนี้ก็ไม่ใหม่แล้ว แต่ว่าสำหรับคนที่เขียนโค๊ดสไตล์เก่าแบบ C++98 มานานก็อาจจะไม่คุ้นเคย &lt;/p&gt;

&lt;p&gt;คอนเซปท์นี้ เมื่อใช้ร่วมกับ datatype ที่ออกแบบมาเพื่อทดแทนการใช้งาน pointer นอกเหนือจากความเป็น pointer (เช่น string, dynamic array) จะทำให้เราสามารถเขียนโค๊ดที่ปลอดภัยได้มากขึ้น ลดอัตราการเกิด dangling pointer และ memory leak ได้มากขึ้นครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.cppreference.com/w/cpp/memory"&gt;Dynamic Memory Management - cppreference.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>thai</category>
      <category>cpp</category>
    </item>
    <item>
      <title>SoLoud - Game Audio Engine ที่ใช้งานง่าย (มาก) และ opensource สำหรับ C++</title>
      <dc:creator>Wutipong Wongsakuldej</dc:creator>
      <pubDate>Sun, 29 Jan 2023 11:20:21 +0000</pubDate>
      <link>https://dev.to/mr_tawan/soloud-game-audio-engine-thiiaichngaanngaay-maak-aela-opensource-samhrab-c-37ea</link>
      <guid>https://dev.to/mr_tawan/soloud-game-audio-engine-thiiaichngaanngaay-maak-aela-opensource-samhrab-c-37ea</guid>
      <description>&lt;p&gt;หลาย ๆ คนที่ยังเขียนเกมแบบเขียนเอนจินเอง น่าจะเคยเขียนส่วนที่เล่นเสียง ไม่ว่าจะใช้ DirectSound, XAudio, SDL2 แล้วรู้สึกว่า ทำไมมันช่างลำบากเหลือเกิน เล่นแค่ให้เสียงออกน่ะง่าย แต่ควบคุมให้มันทำงานได้ดังใจนี่มันช่างวุ่นวาย&lt;/p&gt;

&lt;p&gt;ในทางกลับกัน บางคนก็อาจจะเคยได้จับ Engine เฉพาะ อย่าง AudioKinetic WWise หรือ FMOD มาก่อน แต่ก็สู้ราคาเอนจินไม่ไหว เพราะแพงจับใจ (ถึงไลเซนส์อินดี้จะฟรีก็ตาม)&lt;/p&gt;

&lt;p&gt;วันนี้จะขอแนะนำ &lt;a href="https://solhsa.com/soloud/"&gt;SoLoud&lt;/a&gt; มาเป็น Audio Engine อีกสักตัวนึง แต่ก่อนจะไปถึงขั้นนั้น มาดูกันก่อนว่า ทำไมเราถึงต้องมี Audio Engine หรือ Middleware หรือ Library หรืออะไรต่อมิอะไรที่เราเรียกกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  ความจำเป็นของ Audio Engine
&lt;/h2&gt;

&lt;p&gt;บางคนคงคิดว่า การเล่นเสียงมันไม่น่าจะยาก แค่สร้าง audio data แล้วเรียกฟังก์ชั่นของ OS มันก็เล่นให้ได้แล้ว ในความเป็นจริงคือ เวลาเราสร้าง audio device ขึ้นมาสักตัวนึง เราจะได้สตรีมของ audio มาสายนึง เวลาเราเล่นเสียง ตัว api ก็จะทำการเขียนข้อมูล PCM นี้ลงไปใน stream ที่ว่า&lt;/p&gt;

&lt;p&gt;ถ้าเราเรียกฟังก์ชั่นที่ว่าหลาย ๆ ครั้งติด ๆ กัน ข้อมูลก่อนหน้าที่เคยเขียนลงไปก่อนหน้า จะถูกแทนที่ด้วยข้อมูลใหม่ที่ถูกเขียนลงไปทีหลัง นั่นหมายถึง เฉพาะเสียงสุดท้ายเท่านั้นที่จะถูกเล่นออกไป&lt;/p&gt;

&lt;p&gt;ถ้าเราจะเล่นหลาย ๆ เสียงพร้อม ๆ กัน หรือเหลื่อม ๆ กัน เราจึงไม่สามารถใช้ฟังก์ชั่นที่ว่าได้ตรง ๆ เราจำเป็นจะต้อง mix ทุกเสียงรวมกันให้เหลือแค่เสียงเดียว แล้วจึงเขียนลงไปในสตรีม &lt;/p&gt;

&lt;p&gt;ดังนั้น ฟังก์ชั่นหลักที่สำคัญที่สุดของ Audio Engine คือการผสมเสียงก็ว่าได้ &lt;/p&gt;

&lt;p&gt;ทีนี้ ด้วยความที่ว่า audio เป็นงานที่สัมพันธ์กับเวลา เราไม่สามารถเขียนข้อมูลลง stream เร็วไปหรือช้าไปได้ มันจะต่างกับงาน graphics ที่ เราสามารถวาดเฟรมใหม่ทันทีได้หลังจบเฟรม ถ้าเราทำแบบนั้นในงาน audio จะเกิด stutter คือเสียงมันถูกส่งมาเร็วเกินไปทำให้มีข้อมูลบางส่วนถูกข้ามไป หรืออาจจะส่งมาช้าเกินไปทำให้มีจังหวะที่เสียงเงียบได้อีกด้วย ดังนั้นงาน audio จะมีจังหวะของมันเองและไม่ขึ้นอยู่กับกราฟิค นอกจากนี้เสียงที่ถูกส่งเข้ามาเล่นอาจจะมีขนาดที่ใหญ่กว่าตัวบัฟเฟอร์ของสตรีมนั้นๆ ทำให้จะต้องเก็บข้อมูลบางส่วนเอาไว้ใช้ตอนเติม buffer ครั้งต่อ ๆ ไปหลังจากนั้นอีก&lt;/p&gt;

&lt;p&gt;การเขียนโค๊ดที่ใช้ผสมเสียง หรือที่เรียกว่า mixer จึงเป็นงานที่ไม่ธรรมดา มีความซับซ้อนระดับหนึ่ง และยิ่งมีฟีเจอร์มากเท่าไหร่การสร้างก็จะยากขึ้นเท่านั้น&lt;/p&gt;

&lt;h2&gt;
  
  
  SoLoud คืออะไร ?
&lt;/h2&gt;

&lt;p&gt;SoLoud เป็น Open Source, Cross-Platform Audio Engine ที่พัฒนาโดย Jari Komppa ตัวมันวางตัวยู่บน Audio Backend ต่าง ๆ เช่นพวก SDL2, ALSA, หรือ WinMM แล้วเราสามารถเล่นเสียงได้โดยง่าย และสามารถควบคุมโวลุ่ม ควบคุมการแพน หรือแม้กระทั่งเพิ่มเอฟเฟคอย่างรีเวิร์ปเข้าไปได้ด้วย&lt;/p&gt;

&lt;p&gt;ทีนี้ ถ้าถามว่า ใช้ง่ายแค่ไหน ข้างล่างคือโค๊ดตัวอย่างครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SoLoud&lt;/span&gt; &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Wav&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sfx.wav"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setVolume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แค่นี้ก็เล่นเสียงจากไฟล์ &lt;code&gt;sfx.wav&lt;/code&gt; ไปออกทางซ้าย 20% และเสียงดัง 50% จากต้นฉบับ เรียบร้อย&lt;/p&gt;

&lt;p&gt;และถ้าเราเล่นไฟล์หลาย ๆ ไฟล์พร้อม ๆ กัน SoLoud จะผสมเสียงให้เอง เราไม่ต้องเขียนฟังก์ชั่นสำหรับผสมเสียงละ &lt;/p&gt;

&lt;p&gt;ลักษณะเฉพาะนึงของ SoLoud คือ Library นี้จัดการเรื่อง lifetime ได้ดี ถ้าสังเกตคือตัว interface แทบจะไม่มี pointer เลย ทั้ง ๆ ที่เป็น library ที่ผู้ใช้สามารถขยายความสามารถได้ง่าย อันนี้เกิดจาก Library ออกแบบมาให้เราจัดการ state ทั้งหมดผ่าน stack variable ทำให้เกิด memory leak จากฝั่งคนใช้ได้ยากขึ้น &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Time!
&lt;/h2&gt;

&lt;p&gt;อันนี้เป็นเดโมที่ผมเขียนไว้ ใช้ SDL2 กับ ImGUI ครับ สามารถไปดาวน์โหลดได้ที่ &lt;a href="https://github.com/wutipong/soloud-sdl"&gt;Github&lt;/a&gt; &lt;a href="https://github.com/wutipong/soloud-sdl/releases/tag/v0.1.0"&gt;Release 0.1&lt;/a&gt; มีแต่ Windows นะครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MwLZ64UL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79ylh32v1uy8dxa5k6lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MwLZ64UL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79ylh32v1uy8dxa5k6lv.png" alt="Screenshot" width="880" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เนื่องด้วยจากความขี้เกียจของผม ผมจึงใส่เกือบทั้งหมดลงไปในใน &lt;em&gt;main.cpp&lt;/em&gt; ยกเว้นส่วนที่เป็น file กับ joystick อันนี้ไม่ต้องไปทำความเข้าใจมากก็ได้ครับ อย่างตัว &lt;em&gt;FileSystemFile&lt;/em&gt; นี่สามารถใช้ &lt;em&gt;SoLoud::File&lt;/em&gt; หรือไปใช้พวกฟังก์ชั่นที่อ่านไฟล์ได้เองตรง ๆ ก็ได้ครับ ที่ผมเขียนคลาสนั้นขึ้นมานี่ เขียนมาเพื่อรอง &lt;em&gt;std::filesystem::path&lt;/em&gt; แค่นั้นเลย&lt;/p&gt;

&lt;h3&gt;
  
  
  SoLoud::Soloud
&lt;/h3&gt;

&lt;p&gt;คลาส &lt;em&gt;Soloud&lt;/em&gt; นี้เป็นคลาสหลักของตัว Engine เป็นเหมือนตัว root level device ที่เวลาที่เราจะทำอะไรกับ SoLoud คลาสนี้แหละที่เราจะไปยุ่งด้วย&lt;/p&gt;

&lt;p&gt;คือจะมองว่าเป็นตัวเล่นเสียง + ตัวผสมเสียงในตัวเดียวก็ได้ครับ&lt;/p&gt;

&lt;h3&gt;
  
  
  SoLoud::AudioSource
&lt;/h3&gt;

&lt;p&gt;AudioSource ก็คือคลาสที่เป็นแหล่งกำเนิดเสียง ไม่ว่าจะเล่นจากไฟล์ หรือจะสังเคราะห์ขึ้นมาเอง &lt;/p&gt;

&lt;p&gt;ตัว SoLoud มีคลาสชุด AudioSource ติดมาให้จำนวนหนึ่ง ข้างล่างนี้เป็นตัวอย่างบางส่วนครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Wav&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sfx.wav"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WavStream&lt;/span&gt; &lt;span class="n"&gt;bgm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bgm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bgm01.mp3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Speech&lt;/span&gt; &lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Sfxr&lt;/span&gt; &lt;span class="n"&gt;sfxr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sfxr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadPreset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Sfxr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;COIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;654'321&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bgm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sfxr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Wav เป็น Source ที่เก็บข้อมูลเสียง PCM จากไฟล์ทั้งไฟล์มาเก็บไว้ &lt;/li&gt;
&lt;li&gt;WavStream ต่างกับ Wav ตรงที่ตัวมันเองจะอ่านข้อมูลแบสตรีมมิ่งจากไฟล์ระหว่างการเล่น ทำให้ใช้เมมโมรี่น้อยกว่า แต่ก็แลกมาด้วยจำนวน I/O Operation เพราะมันจะอ่านไปเล่นไป &lt;/li&gt;
&lt;li&gt;Speech เป็นตัวสังเคราะห์เสียงพูดภาษาอังกฤษอย่างง่าย เอาไว้ทำเสียงพูดขำๆ&lt;/li&gt;
&lt;li&gt;SFXR ไลบราลีสำหรับสร้างซาวนด์เอฟเฟคด้วย synthesizer ตัว&lt;/li&gt;
&lt;li&gt;และอื่นๆ&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bus
&lt;/h3&gt;

&lt;p&gt;Bus เป็น Audio Source ที่พิเศษตรงที่ตัวมันเองจะรับเสียงจาก Audio Source อื่น ๆ ก่อนจะส่งไปที่ตัว Soloud object&lt;/p&gt;

&lt;p&gt;Bus ทำตัวเป็น submix ที่เราสามารถนำเสียงที่อยู่ในกลุ่มเดียวกันมารวมกันในจุดเดียว เพื่อที่จะควบคุมเสียงพร้อ ๆ กันได้ในทีเดียว&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aBus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;aBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play3d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ยกตัวอย่างเช่น เกมอย่าง Rockman/Megaman X จะมีเสียงซาวนด์เอฟเฟคต่างๆ  และเสียงเพลง background music เมื่อเรากด pause เสียงซาวนด์เอฟเฟคทั้งหมดจะหยุดชั่วคราว เสียง background music จะเบาลง และเมนูจะแสดงขึ้นมา เมื่อเรากดปุ่ม ซาวนด์เอฟเฟคในหน้าเมนูก็จะดังตามปรกติ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound_efx_bus_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อเรากดปิดเมนู เสียงซาวนด์เอฟคเดิมจะเล่นต่อจากจุดที่หยุดชั่วคราว เสียง background music จะกลับมาดังเหมือนเดิม และเสียงเมนูก็จะหายไป&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound_efx_bus_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตรงเราสามารถทำได้โดยการให้ sound effect ในเกมรันอยู่บน bus หนึ่ง แล้วเรา play/pause ตัวบัสนี้เมื่อกด pause เข้าเมนู/ออกจากเมนู โดยที่เราไม่ต้องไปหยุด sound source ย่อย ๆ ทีละตัว &lt;/p&gt;

&lt;h3&gt;
  
  
  Manipulation
&lt;/h3&gt;

&lt;p&gt;สำหรับ Soloud เราสามารถสั่งเล่น audio source เดียวกันหลาย ๆ ครั้งพร้อม ๆ กันได้ ยกตัวอย่างคือเสียงปืน ที่มักจะเป็นเสียงเดียวกัน และอาจจะเล่นพร้อม ๆ กันหรือเล่นต่อ ๆ กันได้ &lt;/p&gt;

&lt;p&gt;ทีนี้ แล้วเราจะควบคุมเสียงที่เล่นออกมาแล้วได้อย่างไร? เวลาที่เราเล่นเสียง ฟังก์ชั่น &lt;code&gt;play()&lt;/code&gt; จะคืนค่า integer ออกมาตัวหนึ่ง ที่เรียกว่า handle &lt;/p&gt;

&lt;p&gt;เราสามารถใช้ handle ไปควบคุมเสียงที่เล่นออกมาแล้วได้ เช่นอาจจะแพนซ้ายขวา หรือตั้งค่าความดัง เป็นต้นครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;SoLoud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Wav&lt;/span&gt; &lt;span class="n"&gt;wav&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;wav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gunshot.wav"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wav&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setVolume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;soloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 20% to the right.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filter
&lt;/h3&gt;

&lt;p&gt;filter เป็นเหมือนเอฟเฟคที่ทำงานคู่กับ audio source เช่น เราอาจจะอยากให้เสียงพูดมีลักษณะเหมือนเสียงหุ่นยนต์ เราอาจจะอยากให้เสียงเพลงมีลัษณะเหมือน lofi เราอาจจะอยากให้เสียงที่วิ่งผ่านบัสซาวนด์เอฟเฟคมีเสียงรีเวิร์ป ตรงนี้เราสามารถทำได้หมดเลย และง่ายมากด้วย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lofi_filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;aBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;echo_filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ข้อจำกัด
&lt;/h2&gt;

&lt;p&gt;SoLoud ติดต่อกับ backend ด้วยโค๊ดที่เลเวลล่างมาก ตัวมันเองคำนวนตำแหน่งสามมิติด้วยตัวมันเอง แทนที่จะให้ backend คำนวนให้ ทำให้ไม่สามารถใช้ศักยภาพของแพลตฟอร์มได้ดีนัก อย่างพวก 3D Binaural Audio ด้วยการใช้ฮาร์ดแวร์นี่ทำไม่ได้ และตัวมันเองก็ยังไม่รองรับ HRTF ด้วย &lt;/p&gt;

&lt;h2&gt;
  
  
  อุปสรรคในการใช้งาน
&lt;/h2&gt;

&lt;p&gt;SoLoud เป็น library ที่ เท่าที่ดูยังไม่มีใครทำ prebuild ให้ และเหมือนว่าออกแบบมาเพื่อให้เรานำโค๊ดทั้งชุดใส่เข้าไปในโค๊ดของเราเลย อันนี้หลายๆ คนอาจจะไม่ค่อยสบายใจหรืออาจจะรู้สึกว่ามันลำบากนิดนึง ก็นานาจิตตังครับ&lt;/p&gt;

&lt;p&gt;ทั้งนี้เค้ามีก็ premake script ให้แหละ แต่กับคนที่ไม่ใช้ premake ก็ไม่มีประโยชน์อะไรเท่าไหร่&lt;/p&gt;

&lt;h2&gt;
  
  
  ทิ้งท้าย
&lt;/h2&gt;

&lt;p&gt;สำหรับคนที่ยังเขียน C++ อยู่ และอยากจะ modernize โค๊ดตัวเอง เอาโค๊ดของตัวเองที่อาจจะดูและไม่ดีนักออก แล้วเอาโค๊ดที่ดูแลดีกว่าของคนอื่น (ที่เป็นโปรเจคที่ร่วมกันทำงานหลายคน และร่วมกันเป็นเจ้าของ) มาใช้แทน สำหรับภาค Game Audio ผมว่า SoLoud นี่ก็ถือว่าตอบโจทย์ได้ดีทีเดียวครับ ใช้งานง่าย ลูกเล่นเยอะ แถมทำงานได้หลายแพลตฟอร์มอีก&lt;/p&gt;

&lt;p&gt;ส่วนตัวผมรู้จัก SoLoud จากการไปลองศึกษา &lt;a href="https://github.com/ConfettiFX/The-Forge"&gt;The Forge&lt;/a&gt; ที่เป็นเหมือนเฟรมเวิร์คสำหรับเขียนเกม ถูกใช้ในเกมที่วางขายแล้วจำนวนหนึ่ง รวมทั้ง AAA อย่าง Starfield และ No Man Sky คิดว่า โค๊ดที่ถูกใช้ในโครงการระดับนี้ก็น่าจะถูกคัดกรองมาดีระดับหนึ่งแล้วครับ &lt;/p&gt;

&lt;p&gt;อันนี้ใครสนใจก็ลองหามาเล่นดูได้ครับผม&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>gamedev</category>
      <category>gameaudio</category>
    </item>
  </channel>
</rss>
