よもやもダンプ

気が向いた時にアウトプットしておくところ

雑記#68

暑さが辛くて何もやる気が起きていなかったけど,ちょっと最近はマシになった?
相変わらずどこかに出かける気にはなっていないけど,最近は音楽への興味が復活しつつある.
しばらくサッカーだったり旅行だったり節約だったりに精を出していて,あまりスマホで音楽を聴くこともなかった.
別に嫌になったからとかじゃなくて,ただ何となく別にいいかな,と思っていただけなんだけど,それでも音楽を聴こうという気にはなってこなかった.

適当にTwitterのTLを見て暇をつぶしてた時に,曲のサンプルのツイートが流れてきて何となく再生ボタンを押してみたら,こんな曲があるんだと思って数回聞き直してた.
昔bandcampやsoundcloudで曲を漁ってた時の感覚を思い出して,刺激を受けるというか自分のやる気が駆り立てられる感じに久しぶりになった.
新しいものを発見した時の驚きだったり楽しさが音楽にもあるんだなと改めて思ったし,もっと楽しみたいと感じるようになって,自分も音楽に戻りたくなった.

雑記#67

前からJUCEでなんかプラグイン作りたい作りたいと言っていても手を動かしていなかったので,今月は遂にコードを書いてみることにした.
正直フレームワークで用意されているUIのデザインがダサい感じがするのと,UIのコンポーネントを描画するのにレスポンシブデザインが難しいことに不満があるけど,プラグインを作るのに一番重要なオーディオ処理についてはVSTとかAUとかの違いを考えずに用意されたフレームワークを利用するだけで実現できるのが便利だ.
トラッカーを作ったときはRtAudioとRtMidiでそれぞれ別に扱っていたオーディオ処理とMIDI処理もJUCEだと一つのコールバックで扱えるので管理もしやすい.
補助的な機能の対応に時間を割かずメインのことに集中して取り組めるのは,最近部屋が暑すぎてモチベーションは落ち気味の自分にとってはありがたい.
といっても,CMakeやC++20の勉強だったりこれまで見てきたアーキテクチャの実装を試してみたりしているので,全体の開発ペースは相変わらずゆっくりのままだけど.
よくSNSで見る思いつたときにすぐ動いて次の日にはもう形になってるような手が早い人は技術力もやる気もあって凄いと思う.

バイトデータはどの型で保持すべき?

C++でファイルを取り込むときに,ストリームを開いている時間を短くしたいのとインデックスでデータにランダムアクセスしたいので,ファイルのバイトデータを丸ごと配列やコンテナに格納してから内容を解析している. このバイトデータを自分は std::uint8_t の配列で宣言してきたけど,最近標準ライブラリを眺めているとstd::byte なるものが存在することに気が付いた. 便利そうなので使ってみようと思い,サンプルとして人の書いたコードを探していると, char を使っているもの, unsigned char を使っているものがあったりしてだんだんどれを使うべきか分からなくなってきた.
動作としてはどの型を使っても同じことを実現できるのだけども,意味的に使い分けを区別したいというのが自分のこだわりにあるので,候補となりうる型について特徴を調べてみた.なお,C++17以上の環境での使用を想定している.

char

1バイトのサイズを持つ組み込み型.組み込み型なのでヘッダーを何もインクルードしなくても使える.
数値を扱えるが名前の由来の"Character"の通り本来は文字を表す型なので,intのような数値型と同じように扱っていると変数値を文字列に変換するときなどに文字として解釈されてしまい痛い目に合う.

#include <iostream>

int main()
{
    const unsigned char n{0x21};
    // "!" と出力される
    std::cout << n << std::endl;   
    return 0;
}

ちなみに文字列を保持するのにchar*を使ったりするが,Cのコードとの互換性を気にしないのであればstd::stringstd::string_viewを使うのが良さそう.

unsigned char

おそらく多くの場面で慣習的に使われてきた型.組み込み型の中で1バイトのデータといえばこれが思い浮かぶ. 今時気を付ける場面はほとんどないだろうが,注意としてC++では1バイトが8ビットである保証はない1
unsignedと付いているが結局のところ文字型なので,charと同じように場面によっては変数値が文字として扱われる.

std::int8_t / std::uint8_t

<cstdint>ヘッダーで定義されている整数型.8ビットのサイズであることが保証されている.
ただしこの型が定義されるかは処理系依存であるため,環境によっては使用できないかもしれない(ごく稀とは思うが).
また先述のcharunsigned charの型エイリアスとして実装されることが多いため,やはり文字として値を認識されることがある.

std::byte

<cstdlib>ヘッダーで定義されている型.文字でも数値でもなくビット列としてのバイトデータを表す.
ビット演算用の演算子は用意されているが,数値演算は行えない.数値として扱いたい場合はstd::to_interger<T>()を使う.
enum lass byte : unsigned charとして定義されているため,その値の初期化には波カッコによる初期化かstaticキャストによる代入で行う.

#include <iostream>
#include <cstddef>

int main()
{
    const std::byte a{0b0010'0001};
    const auto b = static_cast<std::byte>(0x11);

    const auto c = a | b;
    // const auto d = a + b; はエラー

    // "0x31" と出力される
    std::cout << "0x" << std::hex << std::to_integer<int>(c) << std::endl;

    return 0;
}

void*

void単体では何の意味もなさないが,バイト列を保持するというケースの場合はvoid*を使う手も考えられる.
コールバック関数の引数に任意のデータを渡す際にこの型を使用しているライブラリを見たことがある.
ただしポインターであれば何でも代入できてしまい,C++の静的型付け言語であるメリットが失われてしまうので扱いには注意が必要.
代わりにオブジェクトの寿命の管理が可能かつ不適切な型へのキャストを制限することができるstd::anystd::variant<T...>を使いたい.

使い分け

で,どう使い分けたらいいのか色々探してみると,良さそうな例がいくつか見つかった.

stackoverflow.com

www.reddit.com

個人的には以下のように使い分けるのが良さそう

  • バイトデータが「メモリ空間上のデータ」であり,そのデータの型や構造は不明または問わない(blobなどのデータの場合)
    std::vector<std::byte>
  • バイトデータが数値の羅列であるとき
    → 符号の有り無しに応じて std::vector<std::int8_t>, std::vector<std::uint8_t> などビット幅を明示した型を使う
  • バイトデータが文字列であるとき
    char*もしくはそこからstd::stringstd::string_viewに変換
  • コールバック関数の引数となる汎用型としてのバイトデータ → 許容する型を明示したい場合はstd::variant,そうでない場合はstd::any
  • バイトデータは特定の構造を持つオブジェクトだが,何らかの理由でその型を隠蔽したい(セキュリティ的な理由?) → std::any

標準ライブラリに追加されるクラスを見た感じ,どうもC++は生ポインターを使わなくてもいいようにしていきたい気がするので,できるだけクラスを使ってバイトデータを扱うように考えてみた.
あくまでも考えてみただけの段階なので,実際コードを書いてみるとこのルールではうまくいかないことが多いかもしれない(特に外部ライブラリを使用する場合). そういったところはunsigned charなども使いながらうまく運用していけたらと思う.


  1. 1バイトが何ビットであるかは<climits>ヘッダーのCHAR_BITで確認できる.

雑記#66

これまでC++をメインで触ってきてて,3年に一度仕様が変わるのは知っていたし,C++14,17,20とコンパイラの設定も変えてコンパイルをしつつ新しい機能をつまみ食いしながらコードを書いてきた.
検索して便利そうな機能をコピペで使っていただけだったので,その変更がどういう意図で追加されたのか,どういう場面で使われることを想定されているのかまでは気にしていなかった.
関数を検索するとよくcpprefjp - C++日本語リファレンスに引っかかるけど,最近はこのページをじっくり見つつ,どういった関数・機能がC++で使えるのか勉強しながらコードを書くようにしている.
std::unique_ptrとstd::shared_ptr,std::weak_ptrの使い分けだったり,std::optional,std::any,std::variantの使い分け,constexprやinlineのふるまいなど,これまで何となく使っていた機能がどういうことを想定して実装されたのかを知って,これまでのコードがかなりいい加減に書いていたのか理解してきた.
コーディング規約やアーキテクチャも最近は見るようになりつつコードを書いていくと,部品をガチャガチャつなぎ合わせてもモノはできるけど,中身を整理したうえで組み立てていくほうがすっきりとしたコードになりやすいし,あとで管理もしやすいことが徐々にわかってきた.
仕事のプログラミングはQCDが求められるので今やっていることをこなすのに精いっぱいになってしまうけど,趣味のプログラミングでは自分のペースで進めるので,自分のスキルを磨けるようじっくりと学んで考えてコードを書いていくようにしたい.

雑記#65

今月はイベントがあったり,来月の旅行の計画を立てたりして出費が多くなった.
月に貯金する額を決めておいて先にその分だけ引いているので,その月の使えるお金が少なくなって苦しくなってきた.
食費もケチるため,旅行先でも外食せずにスーパーで買った安いパンを持っていってチビチビ食べる貧乏旅行をしてやり過ごしている.
ただ最近は体力が以前よりも落ちているのか,旅行に行くとすぐ疲れて家に帰ってきても料理する気にならない.
それでも外食はしたくないので家にあるカップ麺などをすする無茶苦茶な食生活をしている.
そんな生活をしていたからか,今月の連休の後半に風邪をひいて39度の熱でぶっ倒れて休みを潰してしまった.
体を壊すとそれこそ余計にお金を使うことになるので,無理のないように過ごさないといけないのだろうけど,心の余裕がなくてお金を使えなくて,余計にストレスが溜まっていく.
こういう時は趣味に没頭するのが一番ともいうけど,最近はその気力すらなくてQOLがかなり低くなってしまっている.
結局お金があれば解決する話なので,どうにかしてお金を手に入れたいとしか考えられなくなってきていろいろ参ってきた.

雑記#64

ここ3か月ずっと料理を自炊していて,調味料も基本的に醤油,塩,胡椒,砂糖,料理酒など基本のものを使って作ってきた.
だけどおととい棚をのぞいたらクックドゥホイコーローの素とか炊き込みご飯の素があったので,たまにはと思い使ってみた.
普段は醤油が大さじいくつ料理酒いくつと混ぜ合わせながら味見して塩を調節したりしていたけど,こういうレトルトの素を使うと何も考えなくても入れただけでおいしいものでできて,なかなか味見が難しくて悩んでいるこれまでの自分の苦労はいったい何だったのかという気持ちになった.
しかもホイコーローとか麻婆豆腐とかはそんな頻繁に作るものじゃないし,そのためだけに豆板醤とかをわざわざ買う気にもなれないので,こういうスポット的に何か作りたいと思ったときは○○の素を買ってみるのもありかなと思った.

料理をするときになにも考えずにとりあえず作り始めることが多くて,最終的によく分からないものが出来上がることが多かった.
けどこの前YouTubeでみた動画で誰かが言っていた「料理を作るのに一番大切なのは,最終的にどんな料理が出来上がるのか味や盛り付けを含めてイメージしながら作ること」という言葉にハッとして,最近は作る前にその食材で何が出来上がるのかを先にイメージしてから料理し始めるようにした.
料理に限らず目的を明確にして,そこに至るまでのプロセスを明確にすると成功に近づくんだなと思った.

雑記#63

これまで全然料理をしたことがないまま生きてきたけど,料理ができるのも人間力の一つだし基本的なことはできるようになりたいと思ったので,今年の目標として料理ができるようになるというのを立てた.
最初は寒かったし簡単なので鍋をずっと作り続けてきたけど,カレー,野菜炒め,肉じゃがなども作るようになってきた.
これまでは出されたものをただ食べるだけで味にも無頓着気味だったけど,自分で料理を作って味を見てみたりすると,何をどれくらい入れたらいいのか調整するのが難しく感じる.
単純においしい・まずいだけじゃなくて,味には塩辛い,甘い,酸っぱい,水っぽいもあるし,コクというかだしの深みというのもあることに少しずつ気が付いてきた.
味を調えるには調味料がいろいろ足りていなかったので少しずつ買いそろえてきたから,作れる料理の幅も広まりそう.
あと,これまではあまり気にしていなかった調理によって素材の味が変わることも気が付いたし,それをうまく使って料理することもやっていきたい.
これまではレシピ通りに作ることに意識していたけど,結局自分にとって満足のいく味のものを作るには目の前の料理の味を見ないと分からないので,味見をしながら試行錯誤して料理の腕を上げていきたい.