upLaTeX: 本日の曜日を日本語で自動的に出力する
やりたいこと
YYYY/MM/DD(曜日)
が欲しいYYYY
は西暦の年MM
は西暦の月DD
は西暦の日曜日
は日本語版の西暦の曜日(Day Of Week,略してDOW)つまり月火水木金土日
BXwareki
は曜日に対応していないらしいjsarticle
でとりあえず確認済
実装
プリアンブルにこれを入れる
\usepackage{datetime2} \usepackage{datetime2-calc} \makeatletter \newcommand{\DOWjpn}{% \DTMcomputedayofweekindex{\@dtm@currentyear-\@dtm@currentmonth-\@dtm@currentday}{\DOWindex}% \ifcase\DOWindex 月\or 火\or 水\or 木\or 金\or 土\or 日\fi% } \newcommand{\DTMjpn}{% \@dtm@currentyear/\@dtm@currentmonth/\@dtm@currentday(\DOWjpn)% } \makeatother
例えばタイトルに入れる場合は
\date{\DTMjpn}
コツ
datetime2
とdatetime2-calc
を流用する- 内部変数の
\@dtm@current
系から年月日を取得できる,しかし肝心の@dtm@currentdow
はなにもしないままだと-1
のままらしい**Current day of week defaults to -1 (that is, ignore it).
from p.121
- 上記を
\DTMcomputedayofweekindex
に入れて曜日の値を取得して(1~7につき月~日)\DOWindex
に保存する - これを実行するとなぜか
@dtm@currentdow
が曜日の値を取得する(0~6につき月~日) - それを
\ifcase
で分岐させて対応する曜日を表示させたい形式で代入する\makeatletter
と\makeatother
間でなので,改行手前に%
を入れないと改行記号が空白扱いされてレイアウトが崩れる- 英語3文字版とかにも応用できそう
TeX Liveユーティリティのリポジトリにあるtabularray.styが使えないので注意
Ventura 13.6.1のアップデートでMBPが壊れた
環境
- MacBook Pro M1 Max 16 inch
- macOS Ventura 13.6->13.6.1
13.6.1とは
- 2023年10月25日発
- セキュリティパッチ
事象
- 寝ている間にアプデをかけた
- 翌朝に確認したら死んでいた
症状
通常の起動
- まずリンゴマークが点灯する
- USB-Cを経由してネットワークに繋ぐと文鎮化する
- 繋げないとリンゴマークが出現してから文鎮化する
- 外付けキーボードorマウスはアクティブ状態である
セーフブート
- 起動オプション通知は出るがその直後に文鎮化する
対応
- 症状としては「ファームウェア破損」が該当した
- まずは別のMacからファームウェアを復活させる
- もしダメなら全データを削除して導入をやり直す
- つまりファームウェアを入れた上でディスクを仕切り直していちからOSを入れてユーザー登録をしなければならない
- それもダメなら基盤が死んでいるので修理が必要
- 現状で配布されている最新のOSはSonomaなので,OSの再インストールを行うと(Time Machineでバックアップを取っていない限り)強制的にVenturaからSonomaに変更される
- ただGenius Barでスタッフ相手に粘るともしかしたらVenturaを落とせるかも?まぁSonomaも安定しているようだけど
生きているApple SiliconのMacを持っている場合
- 生きている方にApple Configurtatorを導入する
- support.apple.com
- App Storeにある,無料で入手できる
- 以下の手順書に基づいて作業する(かなり面倒)
- support.apple.com
- Monterey以前のOSの場合はFInderで操作する
- support.apple.com
- OSを再インストールする(データは飛ばない)
- support.apple.com
- 普通に起動しようとすると文鎮化するらしい
持ってない場合
- 諦めてGenius Barを予約して良きに計らってもらう,ファームウェアの復活は自分のMacを渡してから20分弱で終わる
- OSの再インストールは時間がかかるため持ち帰ってやる,もし失敗したらディスクが飛んでいることになるので修理
macOS Sonomaの使い心地
IEEEtran.clsのConferenceの使い方
IEEEtran.clsを使ってConferenceを投稿したので備忘録
環境
- M1 Mac Ventura
- MacTeX 2023
- homebrewで入手
テンプレート入手
ieee.orgの配布ページ,https://www.ieee.org/conferences/publishing/templates.html
LaTeX TemplateとBibteX Templateがあるので両方を入手する
もしくはTeXLiveのieeetran
に双方が入ってるはずのでそれを使う
いずれにせよ日本語に非対応,pdfLaTeXでコンパイルする,vscodeのlatexmkのデフォルトコマンドで動くかも
つかうもの
IEEEtran.cls
- クラスファイル,最低限これが必要
IEEEabrv.bib
- IEEE関連の固有名詞を展開する
IEEEfull.bib
だと全ての固有名詞が省略形ではなくなるらしい
IEEEtranN.bst
- 文献リストのスタイルを決める
plainnat
スタイルが使える- 普通は
IEEEtran.bst
を使う
natbib
plainnat
を扱えるエンジンIEEEtranN.bst
を読み込む- 普通ならばTeXLiveに入ってる
paper.tex
- 本体
confs.sty
- パッケージ管理や自作スクリプトをまとめる
biblio.bib
- 文献リスト
個人的テンプレート
\documentclass[conference]{IEEEtran} \IEEEoverridecommandlockouts \usepackage{confs} %confs.styで設定を管理する \begin{document} \title{ %タイトル \thanks{} %カネの話をする } \author{ \IEEEauthorblockN{} %名前 \IEEEauthorblockA{ \textit{} \\%分野 \textit{} \\%所属 \\%都市,国家 %連絡先メアド } \and \IEEEauthorblockN{} %名前 \IEEEauthorblockA{ \textit{} \\%分野 \textit{} \\%所属 \\%都市,国家 %連絡先メアド } } \maketitle \begin{abstract} %概要 \end{abstract} \begin{IEEEkeywords} %キーワード \end{IEEEkeywords} \section{} %本文 \appendicies %複数の附録に対応 \renewcommand{\theequation}{\thesection.\arabic{equation}} %数式番号を再定義 \numberwithin{equation}{section} %数式番号の表示を再定義 \section{} %附録 \section*{Acknowledgment} %謝辞 \bibliographystyle{IEEEtranN} %文献リストのスタイルを召喚 \bibliography{IEEEabrv,biblio} %biblio.bibの文献リストをIEEEabrv.bibの補完を適用して召喚 \end{document}
\usepackage{textcomp} % 特殊文字シンボルを追加 \usepackage[numbers,sort&compress]{natbib} %IEEE仕様でnatbibを召喚 \renewcommand{\bibfont}{\fontsize{8pt}{10pt}\selectfont} %文献リストのフォントサイズを定義 \setlength{\bibsep}{0pt} %文献リストの間隔を圧縮 \makeatletter \renewcommand{\@biblabel}[1]{[#1]\hfill} %文献リストの表示を定義 \makeatother \usepackage{float} %図表の配置 \usepackage{here} %図表の強制配置(非推奨) \usepackage{tabularray} %表ライブラリ \SetTblrInner{rowsep=4pt,colsep=4pt} %tblr環境を見やすくする \usepackage{stackengine} %いろんな場所で無理矢理に改行つきレイアウトを入れる \usepackage{amsmath,amssymb,amsfonts,mathtools} %数学ライブラリ \usepackage{autobreak} %数式の自動改行 \usepackage{empheq} %並列した数式を立てる \usepackage{mleftright} %括弧等のスペースを詰める \usepackage{bm} %ベクトル表現に使う \usepackage{siunitx} %単位表現に使う \usepackage{wrapfig} %周り込みで図を入れる \usepackage{caption,subcaption} %図に2層のキャプションを入れる \captionsetup{labelsep=period} %Fig.N:ExplainではなくFig.N.Explainみたいにする \usepackage{graphicx} %図ツール \usepackage{xcolor} %色操作ツール
natbib
を使う理由
推奨されているIEEEtran.cls
とcite
とIEEEtran.bst
のコンビでは,一箇所に複数の文献を配置するとそのレイアウトが圧縮されない,cite
の仕様としては圧縮されるべきところIEEEtran.cls
と一緒に使うと圧縮禁止オプションもなしに強制的にそうなってしまう,なぜならそれがIEEEのスタンダードスタイルらしいので
要するに[1][2][3][5]を[1-3,5]としたい時はcite
ではなくnatbib
とIEEEtranN.bst
を使いながら文献リストのスタイルを自力で修正する必要がある
appendices
の数式番号
({セクション番号}.{メイン数式番号}({サブ数式番号}))
の形式を取る,A.1とかA.2aとかそんな感じになる
macOSでモブ・コントロールをやる時にMintegralを使用した広告が出現すると進行不能で詰む
結論から言うと,バトル終了後の3倍目押し広告は,macOSでやるなら絶対に押すな
なお普通の広告でもたまにバグるので,Apple Siliconではそれを覚悟してプレイしろ
もしくはスキップ券を買え(完全なる対処法は実質的にそれしかないと考えられる)
macOSのみ
Mintegral進行不能バグ
Mintegralは広告でユーザにゲームの簡易版をプレイさせるSDK
30秒広告(報酬広告)でゲーム関係の場合に5割程で出てくる
パズルゲー(例えばRoyal Matchの一部)が該当するカスがよ
単純にアフィのリンクが出るタイプでは使われないので運ゲー
進行不能であるため強制終了が必須(⌘+Qでは終了できない)
なぜかバトル終了後の広告に出現率が偏っている気がするが…
ちなみにスマホアプリであれば問題ない,理由はわからない…
アプリ内ブラウザ進行不能バグ
広告画面を閉じた後でブラウザを開かせるタイプの広告に注意
何も表示されないウィンドウが出現した際に完了を押すと発生
進行不能であるため強制終了が必須(⌘+Qでは終了できない)
ちなみにスマホアプリであれば問題ない,理由はわからない…
リザルト途絶バグ
バトル終了後に広告を再生してからリザルトに遷移する時点でフリーズする
アプリそのものがハングするため強制終了が必須(⌘+Qでは終了できない)
広告途絶バグ
そもそも任意の広告のロード時にフリースする可能性が否定できないという
アプリそのものがハングするため強制終了が必須(⌘+Qでは終了できない)
ブースター開封バグ
ドロップされたブースターを開封する時点から操作不能となる
進行不能であるため強制終了が必須(⌘+Qでは終了できない)
枚数更新は反映されたりされなかったりするが原因条件は不明
共通
1面タイマン堤防破壊バグ
1面タイマンでは堤防を規定値で破壊してから根城を破壊する
爆撃スキルで堤防と根城を同時に破壊するとクリア不能になる
なぜか堤防も根城も消滅しないがフラグ管理がおかしいのでは
とりあえずはホームボタンで仕切り直すことで脱出ならできる
2面タイマン同時討伐バグ
2面タイマンでは2つの根城を両方とも破壊する(1 vs 1 vs 1)
だが同時に破壊すると根城が消滅しボーナス稼働が不発する
どうしようもないので観念してリザルト画面を待つしかない
もしくは順番に各個に破壊する技術を身につける必要がある
討伐順番非正規バグ
小型根城と大型根城があるコースで小型根城を破壊せず大型根城を破壊するとクリア不能になる
自分と相手のDPS差が大きい時に本丸を先に破壊する,または爆撃スキルで本丸を先に破壊する
通常コースの2週目もしくはBOSSコースで起こる,順番に破壊する技術を身につける必要がある
とりあえずはホームボタンで仕切り直すことで脱出ならできる,フラグ管理がおかいしいのでは
カード枚数バグ(仕様?)
Apple ID等でアカウントを作成しても,カード枚数は端末ごとに保存されており,全体で同期されることはない
カードレベルおよびコイン枚数の更新バグ(仕様?)
ログインの際にカードレベルとコイン枚数のデータがサーバから端末上へと同期される
しかしサーバ上のカードレベルとコイン枚数のデータは端末から一方的に上書きされる
つまり複数の端末で同時にログインして種類が被らないようそれぞれからカードレベルを上げる処理をすればコイン枚数の消費量を減らせる(規約的にギリギリかもしれない?)
個人的なおすすめ攻略法
- 以下のコースを選り好む(シールドブレイクのパターンは報酬がカスなので確実に回避)
- 1面タイマン(回転仕切コースにおいて以下の構成ではエイリアンが天敵なので要注意)
- 2面タイマンの通路コース(仕切コースはボーナス稼働時にモブが移動できないので×)
- BOSS(できれば舟形が良い,これがレンガもコインもカードも最もよく稼げると思う)
- ビック・パーサ(ボーナス稼働時にDPS◎)
- Nexus(ボーナス稼働時にDPS◎,砲台化)
- 攻撃型カード(ノーミー,クマ,ソルジャー,騎士)(ボーナス稼働時にDPS◎)
- 30秒広告で連射2倍(ビック・パーサでのクリアに必須かも)
- ボーナス稼働でコインとカードをゴリゴリ稼ぐ(これがミソ)
追記(6/14)
アプデで対策されたらしく草 クリア時の通信が強制的に遮断されるようになりMac周回の旨みが死んだ ただ現状ではまだ高級ルーレットが回せる強みがあるので物事は考えよう
C++ 継承のprivate / protected / publicの切り替え
アクセス権限の意味
アクセス指定子をつけると設定できる
private
該当する関数または変数には,それらが実装されたクラス(のメソッド)のみアクセスできる
protected
該当する関数または変数には,それらが実装されたクラス(のメソッド)とそのクラスから派生したクラス(のメソッド)のみアクセスできる
public
制限はない,オブジェクトを無から生成して渡すためにpublic
の関数を経由する必要がある
アクセス権限の規制
継承時にアクセス指定子をつけると継承元の関数または変数のアクセス権限にリミットがかかる 隠蔽する方向にのみ動作する
private
public
とprotected
がprivate
になる
class A{ private: int x; // can't acccess from B protected: int y; public: int z; } class B:private A{ // y -> private // z -> private }
protected
public
がprotected
になる
class A{ private: int x; // can't acccess from B protected: int y; public: int z; } class B:protected A{ // y -> protected // z -> protected }
public
制限はない
class A{ private: int x; // can't acccess from B protected: int y; public: int z; } class B:private A{ // y -> protected // z -> public }
アクセス権限の変更
using
と::
でスコープをつけながらエイリアスを作成する際,アクセス指定子を先頭につければ個別に上書きできる
昇格
protected
からpublic
にする
class A{ protected: int x; } class B:public A{ public using A::x; // reachable A.x from public, protected, private }
使い道
特に思いつかないし,こんなものが必要になるような場面に遭遇したら,ひとまず設計を修正した方が良い気がしています
一括で昇格させる方法
これがどうやら存在しないらしい.やるならば全てに一対一でpublic using Class::name
を叩く必要があるらしい
降格
public
からprotected
にする
class A{ public: int x; } class B:public A{ protected using A::x; // reachable A.x from protected, private }
public
からprivate
にする
class A{ public: int x; } class B:public A{ private using A::x; // reachable A.x from private }
protected
からprivate
にする
class A{ protected: int x; } class B:public A{ private using A::x; // reachable A.x from private }
使い道
部分的にprotected
をprivate
にしたいです,特に組込実装ではコード領域すらも節約する必要があるかと思います
一括で降格させる方法
つまり継承時にアクセス権限の規制をやれということで
多重継承
名前衝突や菱形問題の原因になるのでやめたい もしくはせめて仮想継承をちゃんと実装したい
名前衝突
衝突しなかったらスコープ無しで派生クラスから呼べる 衝突したらスコープ派生元クラスを解決する必要がある もし衝突してもエイリアスの作成は1個までならセーフ
class A{protected:int x;}; class B{protected:int x;}; class C:public A, public B{ public: using A::x; // -> OK // public: using B::x; -> error }; int main(void){ C c; c.x = 0; }
言い換えれば権限の変更は1個しかできないということ むしろ今までのケースが「衝突しなかったらスコープ無しで派生クラスから呼べる」のを悪用して同名の再定義をしている
菱形問題
これらの例はA
とB
にそれぞれ独立にx
があった上でC
に合体させたせいで詰んでいるが,x
を持つA
からB
とC
に派生させた上でD
に合体させるような場合,(B
とC
が悪さをしない限り)仮想継承でx
は何とかなるかも
仮想継承
#include <iostream> using namespace std; class A{ protected: int x; }; class B: virtual public A{}; class C: virtual public A{}; class D: public B, public C{ private: using C::x; protected: using B::x; public: using A::x; // both lines are equivalent in operation (why?) // `public` is needed to be specified in the end }; int main(void){ D d; d.x = 1; //d.A::x = 1; -> error if A::x is placed in protected cout << d.x << endl; };
A::x
の実体をB::x
もC::x
もD::x
も指すが,アクセス権限はA::x
とB::x
とC::x
とD::x
でそれぞれ設定され,かつusing
でエイリアスを作成するとその都度に設定が上書きされる(これでも実体は1つなので問題ない)
最終的にD
でpublic: using
するとD.x
を介して実体を操作できる
mixiの呟きをブラウザコンソールから自動削除
これをコピペして連打すればOK
var elementClass; var elementClassArray; var button; elementClass = document.getElementsByClassName("deleteIcon"); elementClassArray = Array.from(elementClass); elementClassArray.forEach((element,index)=>{ element.click(); button = document.getElementsByClassName("formBt01 submitButton"); button[0].click(); }); window.location.reload();
reload();
自動化やるのはダルい
macOS Venturaをアプデしたら暗号化HDDのパスワード解除後のマウントが不可になった件
問題
- HDDのコンテナ構造だけは認識できる
- 暗号化のパスワード解除は可能である
- 解除した後のマウントが不可能である
エラーメッセージ
- 対象コンテナをマウントしようとする->com.apple.diskmanagement.disenter エラー 49218
- 対象コンテナをfirst aidしようとする->apfs_root: btn: invalid o_cksum
環境
- MBP2021 M1 Max 16inch
- macOS Ventura 13.3.1 (22E261)
- BUFFALO Portable HDD
対処方法
first aidするべきは該当のコンテナではなく1つ上のレイヤのコンテナである
例えばHDDの物理イメージがdisk4
だとしたら,その下にコンテナとしてdisk4sX
がいくつかあり,パーティションとして仮想イメージのdisk5
やdisk6
がそれらを指している
おそらく問題が起こるのはその下のdisk5s1
やdisk6s1
だが,こいつにfirst aidしても上記のメッセージが出て解決しない,チェックサムが壊れたのかその仕様が変更されたのか…
そこでdisk5
やdisk6
にfirst aidするとdisk5s1
やdisk6s1
が破損していると認識して修復しようとしてくれる,あとは復活するのをお祈りする,そこそこ時間がかかるので注意
AppleのARM系のチップのDarwinのrootを走ってるXNUの型番について
Darwinとは
macOSの下の方で走っているOSのこと
最後に完全に公開されたのはmacOS 10.13.3の時で,以降はPure Darwinにフォークされて断絶しているっぽい
github.com
Wikipediaでもこの程度
en.wikipedia.org
強いて言えば最新版は22.3.0だということが確実にわかってるくらいか?
XNUとは
Darwinの下の方で走っているkernelのこと
macOSの下のDarwinの下のルートにいる
こっちは今も更新されてる可能性がある
github.com
X is not UNIXという意味らしい,はえ〜
検証
やり方
terminalで
uname -a
みんなの結果を教えてね!
M1 Max
俺のMacbook Proから持ってきた
Darwin 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000 arm64
M2 Max
Bingでヤケクソで「"release_arm64" "M2" macbook pro」で検索したらこんなものが見つかった
Darwin 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:39:46 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6020 arm64
こないだ出たM2 Macbook Proか github.com ちなみにGoogleではこのページがヒットしなかった,冗談抜きにBingの方が検索能力が高いかも…
intel macだと?
最近のヤツなら
RELEASE_X86_64
だが,昔のやつだと
RELEASE_I386
というのもあったらしい
それより前はわかんない
Twitterの目撃情報
「"Darwin" "RELEASE_ARM64"」やら「"Darwin" "RELEASE_X86_64"」やら「"Darwin" "RELEASE_I386"」で検索するとけっこう出てくる
iOSから調べていたら興味深いページを見つけた
多分ここにほとんど網羅されている? www.theiphonewiki.com
まとめ
- macOSとDarwinのバージョン変更は現在ではリンクしている
- Darwinのバージョンが同じでもチップ依存でXNUの型番が違う場合がある
- T8000番台はモバイルまたはノート向け,T6000番台はラップトップ向け,だと思われる
- 通常のM1やM2は,ラップトップであってもT8000番台が振られる
- M1 ProとM1 Max(とM1 Ultra?),M2 ProとM2 Max(とM2 Ultra?)は,搭載する機種がノートであってもT6000番台が振られる
- つまりProチップやMaxチップでなければ「真のMac」とは言えない可能性が微レ存…?
- ただしintel macはI386かX86_64のどちらか
- iOS,iPadOS,watchOSは複雑すぎて意味不
急いで認定整備済品のMacBook Pro 2021を買え
概要
MacBook Pro (15-inch, 2016)がもうすぐ発売から7年が経過して諸々(OSや修理など)のサポートが切れるので,2023年3月にMacBook Pro (16-inch, 2021)の認定整備済品を買った.
2023年1月にM2シリーズのモデルの発売が開始したが,その余波でM1シリーズのモデルの価格に値下げラッシュが始まった.
また2022年後半には円安が進行し,クソ政府のせいで将来的な円高要因や対物価比の賃金上昇兆候がほぼ見出せない.
M2はM1に対してスペックはマイナーアップデート程度であり,比で考えても値上がり幅が酷すぎるせいでコスパが良くない.
2021年発売モデルが中古化して認定整備済品が値下げした今,Intel系MacからApple Silicon系Macに乗り換えるべきである.
さらに,Venturaも最初はバグまみれだったが,そろそろメーカーもM1かつVenturaに対応するリリースを出す頃合いだろう.
ちなみに,MacBook Pro (15-inch, 2016)はまだビンテージ品の扱いなので,やろうと思えばまだ修理は可能…かもしれない
ぶっちゃけ,またキーボードが壊れたし,OSやAppが高機能化しているせいでファンがよく回るし,常用するには辛すぎた
選定スキーム
個人的な要件
- 画面 15inch以上
- 長時間の利用なら大画面の方が良いし,レイアウトの関係でスピーカーの音質も良いし,筐体やファンの放熱量も高いし,チップの性能も上が選べるし…
- RAM 32GB以上
- 16GBで5年ほど使った結果,オンライン会議をしながら必要の資料を随時に引っ張り出すには少し足りなかったし,趣味の創作なんてもってのほかだった
- SSD 512GB以上
- 256GBで5年ほど使った結果,SSD寿命を20%ほど消費した,あと趣味のツールとかを入れると普通に足りなかった,仕事用PCはSSD1TBを支給されてる
- GPU 強め
- 以上を満たすスペックのうち投資効率が良さそうなもの
- 認定整備済品の価格設定を吟味する必要がある!
- できればUS配列…だが認定整備済品は無理かもしれない
- まぁ普段は安い外付けのキーボードを使い捨てればOK
まずこのページを見ろ
Macの認定整備済品リストから要件を満たすものを列挙させている
取り敢えず価格の安い順で確認だ
どういう戦略で選んだのか?
M1 ProとM1 MaxではCPUのCore数は全く同じなので比較しない
ただ「M1 ProかM1 Maxか」と「SSDの容量」「RAMの容量」と「SSDの速度」「RAMの速度」に関係があるらしい(俺はLPDDR5-6400というだけで満足)
最安モデルは318800円
GPU 16Core,RAM 32GB,SSD 512GB
ちなみにこの記事を書いた時点でシルバーは売り切れている
次に来るのは339800円…だが,
こっちはGPU 24Core,RAM 32GB,SSD 512GB
こっちはGPU 16Core,RAM 32GB,SSD 1TB
つまりどちらかを選んでねということである
その次に来るのは354800円
そして問題なのがその次の360800円
これはGPU 24Core,RAM 32GB,SSD 1TB
これはGPU 32Core,RAM 32GB,SSD 512GB
…俺の読み間違えでなければ,性能と価格の順序がここで崩壊していることになる…なぜ?
ちなみに次は380800円なのだが,
これはGPU 24Core,RAM 64GB,SSD 512GB www.apple.com
これはGPU 16Core,RAM 32GB,SSD 2TB
となっており,そしてここから先のGPU 32Coreを持つオプションは一気に値上がりする,いずれもSSDとRAMの増設の影響をモロに受けてるのかもしれない
最終的に購入したオプション
つまり,これ
が良いと判断した
- MacBook Pro (16-inch, 2021)
- CPU 10 (E2+P8) Core
- GPU 32 Core
- RAM 32 GB
- SSD 1TB
my new gear...
考察
いやまったくわからん,どうしてこうなってるのかなにも想像がつかん
バグかと思ったが頼んで届いたらまんまのスペックだったし夢でもない
SoCだし普通に考えればチップまでイジる再整備なんてあるわけがない
だからパーツの価格がどうこうなんてロジックは存在しないはずだろう
ともかく性能が上位互換で尚且つ安価なら明らかに買うべき品物である
使用している感想
MBP2016と比較して
- 音質良し,画質良し,見た目良し,ハードウェア的には完全な上位互換
- でも排気口周りの構造が少し気になったかも
- まぁほとんどファンが回らんから問題なし
- あとキーボードが周り真っ黒で見づらいかも
- 交換のしやすさ考えたらしょうがないかも
- MBP2016はマジで修理の敵だったらしいし
- でも排気口周りの構造が少し気になったかも
- 書類仕事なら何やっても熱くならない
- OBSを回してもほぼファンが回らない
- MainStageが設定を盛っても楽に動く
- Clip Studio Paintの描画が爆速で動く
- ChromeもSafariもEdgeも爆速で動く
- ブラウザの表示が遅いのはネットワークではなくGPUとメモリのトロさのせいだったらしいことに気づいた
- Venturaのバグ?不安定動作?がまだあるっぽい
- JIS配列クソ使いづらい,コーディングに不向き
- ベゼルが薄型化していて良い
M字ハゲだろ - やっぱTouch Barなんて要らんかったんや!
- MagSafeに電力系を分けたのは英断だと思うよ
- でもUSB-Cが4つの方が好き,SDXCいらん
- HDMIポート標準装備は評価できる
でも認定整備済品って中古だろ?
そうらしいが,取り敢えず届いてすぐにhomebrew
とsmartctl
を最速で導入して,そのままSSDのSMART情報を引っこ抜いてきた,その時の結果を貼っておく
% smartctl -x /dev/disk0 smartctl 7.3 2022-02-28 r5338 [Darwin 22.1.0 arm64] (local build) Copyright (C) 2002-22, Bruce Allen, Christian Franke, www.smartmontools.org === START OF INFORMATION SECTION === Model Number: APPLE SSD AP1024R Serial Number: 0ba01912c148e807 Firmware Version: 873.40.4 PCI Vendor/Subsystem ID: 0x106b IEEE OUI Identifier: 0x000000 Controller ID: 0 NVMe Version: <1.2 Number of Namespaces: 3 Local Time is: Tue Mar 7 16:57:40 2023 JST Firmware Updates (0x02): 1 Slot Optional Admin Commands (0x0004): Frmw_DL Optional NVM Commands (0x0004): DS_Mngmt Maximum Data Transfer Size: 256 Pages Supported Power States St Op Max Active Idle RL RT WL WT Ent_Lat Ex_Lat 0 + 0.00W - - 0 0 0 0 0 0 === START OF SMART DATA SECTION === SMART overall-health self-assessment test result: PASSED SMART/Health Information (NVMe Log 0x02) Critical Warning: 0x00 Temperature: 25 Celsius Available Spare: 100% Available Spare Threshold: 99% Percentage Used: 0% Data Units Read: 1,246,833 [638 GB] Data Units Written: 974,955 [499 GB] Host Read Commands: 21,527,048 Host Write Commands: 11,271,572 Controller Busy Time: 0 Power Cycles: 348 Power On Hours: 8 Unsafe Shutdowns: 18 Media and Data Integrity Errors: 0 Error Information Log Entries: 0 Read 1 entries from Error Information Log failed: GetLogPage failed: system=0x38, sub=0x0, code=745
Appleによって1度リセットされた上で動作テスト等などが行われたのか,それとも前の使用者の状態をそのまま引き継いだのか,ちょっとこれではわからない
総Write量が500GBだが,これは明らかにmacOS Venturaのサイズ(13GB)より大きく,Appleがそれだけテストしているとも思えない,訊くしかなさそうだ…
"再帰関数の大川隆法です.スゥ…「" itakogei(); "」"
#include <iostream> using namespace std; void itakogei(){ cout << "再帰関数の大川隆法です.スゥ…「"; itakogei(); cout << "」" << endl; } int main(void){ itakogei(); }
翻訳単位と名前空間と`using`
※初歩的な勘違いをしていたのでメモ
次のコードは動かない
といってもリンクができないだけで分割コンパイルはできる(できてしまう)
#pragma once namespace Hoge{ typedef float hoge; hoge setHoge(); void getHoge(hoge value); };
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; hoge setHoge(){ return ((hoge)10); } void getHoge(hoge value){ cout << value << endl; }
#include <iostream> #include "hoge.hpp" using namespace Hoge; int main(){ hoge x = setHoge(); // x = 10 getHoge(x); // print 10 return 0; }
make: clang++ -std=c++20 -c hoge.cpp main.cpp nm hoge.o > hoge.sym nm main.o > main.sym clang++ hoge.o main.o -o test
clang++ -std=c++20 -c hoge.cpp main.cpp nm hoge.o > hoge.sym nm main.o > main.sym clang++ hoge.o main.o -o test Undefined symbols for architecture x86_64: "Hoge::getHoge(float)", referenced from: _main in main.o "Hoge::setHoge()", referenced from: _main in main.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [make] Error 1
なぜか?
上のスクリプトで分割コンパイルすると,それぞれ#include "hoge.hpp"
が展開された上で翻訳単位としてhoge
とmain
の2つが発生して,それぞれが処理されてシンボルテーブルを持つオブジェクトとして出てくる
main
側はHoge
周りの実装をしていないため,hoge.hpp
の宣言した内容に期待して,名前空間Hoge
を含めたシンボルをあらかじめ設定する
ゆえにhoge
側は名前空間Hoge
を含めたシンボルを実体に持つような実装が期待されており,そうでなければリンク時に名前を解決できず失敗する
ソース側でusing namespace Hoge
を利用して(名前空間の解決の記法を省略して)実装することは不可能であり,それはコンパイラが名前空間の探索と適切な配置よりも先に勝手に大域的な配置をしてしまうためである…
なんでそうなのかは知りませんが,歴史的な経緯とかあるのでしょうか?やっぱり外部リンケージがデフォルトな言語だからなんですかね,extern
とconst
も難解すぎてこのままではstatic
おじさんになってしまう…
hoge
側
nm hoge.o | grep Hoge
0000000000000010 T __Z7getHogef 0000000000000000 T __Z7setHogev
main
側
nm main.o | grep Hoge
U __ZN4Hoge7getHogeEf U __ZN4Hoge7setHogeEv
解決策
namespace Hoge{}
の中に実装を書く
多分これが一番楽だと思います,ただしクソデカネストが許容できる場合の話,俺は辛い耐えられない
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; namespace Hoge{ hoge setHoge(){ return ((hoge)10); } void getHoge(hoge value){ cout << value << endl; } }
実装の名前に必ずHoge::
をつける
using namespace Hoge;
しつつクソデカネストを使わないなら,多分これが一番楽だと思います
また宣言されたtypedef
やclass
の名前スコープを無視できる,人によっては折衷案に見える?
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; hoge Hoge::setHoge(){ return ((hoge)10); } void Hoge::getHoge(hoge value){ cout << value << endl; }
c++に限界を感じたのでAtCoderに入門した
何に限界を感じたのか?
諸事情あってc++でクラス設計をやるようになったが,なんというかアルゴリズム的なものを全く使わないタイプの実装ばっかりやっており,せいぜい自作の行列クラスをどう設計するか程度だった
普段の作業に飽きてきてc++そのものが嫌になり始めたので,気分転換にとでもAtCoderの練習コースで遊び始めた
つまりはマジモンの初心者なので,今までまともに扱うことのなかったシェルのIOについての学習からやっている
何をやっているの?
これのEX22までやって肩慣らしを始めたつもりでいる
…結論から言うと「やべぇアルゴリズムなんもわからんかも」という状態であり,そもそも組込実装とは要求されている要件が違いすぎるためである
クラス設計にそういう要素を組み込むのは確かに面白いと感じたが,それ以上に問題がハードになっていくにつれてこの作業に命を賭けるのは正直キツいよなぁと思った
自作クラスを実体としてメンバ変数に持つ時の挙動
概要
前回のあらすじ
分割コンパイルでモジュール開発をする場合は,ライブラリのシンボル指向実装とinclude
地獄の回避可能なヘッダ記述手法の都合から,モジュールが異なる(レイヤが1つ下の)自作クラスをメンバ変数に持つなら,実体ではなくポインタで持った方が良く,かつそれらはnew
・delete
を使用してメモリ管理をちゃんと実装し,コンストラクタもデストラクタも確認するべし,的な結論に至った(c++98
縛りゆえスマートポインタ不使用)
しかしモジュールの開発においては,同一レイヤのレベルで自作クラスを実装して,そのまま自作クラスのメンバ変数に組み込む,みたいな場合があるかもしれない(,むしろそれが基本的なクラス設計のやり方なのでは…?)
今回のあらすじ
- 同じモジュール内で実装したクラスでも,ポインタで持つ方が安心できる
- 面倒でもオブジェクトは
new
・delete
する癖をつけた方が良いかもしれない - そもそもそういうやり方を統一した方が混乱しない,組み込み型じゃないなら尚更
- 面倒でもオブジェクトは
- 実体で持つ場合は必ず
=
の定義を確認するべし- 例えば,「「クラス
x
」を抱えるクラスy
」をオブジェクトにする場合,クラスy
のコンストラクタが走る前にクラスx
のデフォルトコンストラクタが走る,自作クラスを実体で持つ限りこの挙動は避けられない - クラス
y
のコンストラクタ内でクラスx
のコンストラクタを叩き,その結果をメンバ変数に=
で接続すると,クラスの初期化と同様に反映できる気もするが,これは初期化の文法ではなく代入の文法として動作する - つまり演算子
=
が曖昧な自作クラスを実体としてメンバ変数に持つと予期せぬ動作が起きるかも…- 自作行列ライブラリ(公開できない)の開発中にコレに遭遇して,初めてちゃんと仕様を確認した…
- 1つ下のレイヤのオブジェクトのメンバ変数を叩いて加工するよりも,最初からそのオブジェクトに固有の演算子
=
を定義した方が,実装の責任境界が明確だし後で他のクラスに使われる時も好都合だしで生産性がある
- 例えば,「「クラス
コピー代入演算子のみを扱う,ムーブ代入演算子は扱わない ただし速度向上を考えたら後者の方が良いのかもしれない…
演算子=
のオーバーロードが必要なケース
- 他のクラスに実体で持つ予定のクラス
- そうじゃないなら要らないかも…
- 算術演算みたいな使い方をするクラス
- 行列クラスとかそもそも不可避…
- 引数の設定
- メンバ変数にポインタを持つクラス
new
・delete
またはmalloc
・free
でメモリ確保したポインタを持ってその中でデータを保持している場合,ポインタをコピーするのかポインタの中身をコピーするのかちゃんと考えて実装しよう
実装
. ├── bin │ └── main ├── libDataBase │ ├── inc │ │ └── libDataBase.hpp │ ├── lib │ │ └── liblibDataBase.a │ └── src │ ├── framework.cpp │ ├── framework.hpp │ ├── interface.cpp │ └── interface.hpp ├── makefile └── src └── main.cpp
SHELL = /bin/zsh MAIN_SRC = src/main.cpp MAIN_OBJ = main.o MAIN_BIN = bin/main GCC_BIN = clang++ GCC_FLG = -std=c++11 -O2 make : make libDataBase make main libDataBase : FORCE $(call setAllVar,libDataBase) $(GCC_BIN) $(GCC_FLG) -c $(libDataBase_SRC) @ar cr $(libDataBase_OBJ) *.o @rm -f *.o $(shell echo '#pragma once' > $(libDataBase_REP);) $(foreach hed,$(libDataBase_HED),$(shell echo '#include "$(hed)"' >> $(libDataBase_REP);)) main : FORCE $(call setRefVar,libDataBase) $(GCC_BIN) $(GCC_FLG) -c $(libDataBase_INC) $(MAIN_SRC) $(GCC_BIN) $(GCC_FLG) -o $(MAIN_BIN) $(MAIN_OBJ) $(libDataBase_LIB) @rm -f *.o FORCE : define setAllVar $(eval $1_HED = $(notdir $(wildcard $1/src/*.hpp))) $(eval $1_SRC = $(wildcard $1/src/*.cpp)) $(eval $1_OBJ = $1/lib/lib$1.a) $(eval $1_REP = $1/inc/$1.hpp) $(eval $1_INC = -I$1/inc -I$1/src) $(eval $1_LIB = -L$1/lib -l$1) endef define setRefVar $(eval $1_OBJ = $1/lib/lib$1.a) $(eval $1_REF = $1/inc/$1.hpp) $(eval $1_INC = -I$1/inc -I$1/src) $(eval $1_LIB = -L$1/lib -l$1) endef
- 適当にデータみたいなクラスを定義する
- コピー代入演算子は
void operator=(const claassName& arg)
でやった - メンバ関数に非破壊宣言の
const
をつけるときは先頭(戻り値)ではなく末尾- 戻り値に
const
をつける関数に意味があるのかは正直よくわかっていない - コピー代入演算子の引数に
const
をつけると,引数arg
を変更しないという宣言になるが,その処理でarg
の持つ関数を呼び出す場合は,それがarg
自身を変更しないことをコード上で保証する必要があるため
- 戻り値に
- 比較のためにデフォルトコンストラクタ・コピーコンストラクタ・デストラクタの全てでログを吐かせる
#pragma once #include <string> namespace database{ class framework{ private: int ID; std::string Name; int Year; public: framework(); framework(int id); framework(const framework &arg); ~framework(); void operator=(const framework &arg); void setName(std::string name); void setYear(int year); int getID() const; std::string getName() const; int getYear() const; }; }
const
に設定したgetXXX()
を使用してconst
で引数を取れるコピー代入演算子を作成する
#include <iostream> #include "framework.hpp" using namespace std; using namespace database; framework::framework(){ cout << " framework: Default Constructor, ID: " << -1 << endl; ID = -1; Name = ""; Year = 0; } framework::framework(int id){ cout << " framework: Normal Constructor, ID: " << id << endl; ID = id; Name = ""; Year = 0; } framework::framework(const framework &arg){ cout << " framework: Copy Constructor, ID: " << ID << endl; ID = arg.getID(); Name = arg.getName(); Year = arg.getYear(); } framework::~framework(){ cout << " framework: Destructor, ID: " << ID << endl; } void framework::operator=(const framework& arg){ cout << " framework: Substitute, ID: " << ID << " -> " << arg.ID << endl; ID = arg.getID(); Name = arg.getName(); Year = arg.getYear(); } void framework::setName(string name){ cout << " Set Name: " << name << endl; Name = name; } void framework::setYear(int year){ cout << " Set Year: " << year << endl; Year = year; } int framework::getID() const{ cout << " Get ID: " << ID << endl; return ID; } string framework::getName() const{ cout << " Get Name: " << Name << endl; return Name; } int framework::getYear() const{ cout << " Get Year: " << Year << endl; return Year; }
- 適当にデータベースみたいなクラスを作る
<map>
を使ってみた,あとイテレータを使用せずforeach
を使いたかったので-std=c++11
を指定したmap
自体はint
なid
とframework*
なデータを持つteamData
に使用addMember
,eraseMember
,showMember
をAPIみたいにして実装
_id_latest
は吐き出したID
の最新の値を指す,ガバガバ実装なのは許して
#pragma once #include <string> #include <map> #include "framework.hpp" namespace database{ class interface{ private: framework myData; std::map<int, framework*> teamData; int _id_latest; public: interface(); interface(std::string name, int year); ~interface(); int addMember(std::string name, int year); void eraseMember(int id); void showMember(int id); }; }
map
のデータ構造がよくわからなかったので,ポインタで持つ処理を採用した- 比較用に実体で持つオブジェクトも同時に1つ用意した,挙動を確認しよう
- デストラクタのメモリ解放処理を簡単に書きたいがためだけに
c++11
を選んだfor (const auto& member: map){}
が書けるのはc+11
以降…?auto
のところはちょっと面倒だったので厳密な書き方をしなかっただけ
#include <iostream> #include "interface.hpp" using namespace std; using namespace database; interface::interface(){ cout << "interface: Default Constructor" << endl; } interface::interface(string name, int year){ cout << "interface: Normal Constructor" << endl; myData = framework(0); myData.setName(name); myData.setYear(year); _id_latest = 0; interface::addMember(name, year); } interface::~interface(){ cout << "interface: Destructor" << endl; for (const auto& member: teamData){ delete (member.second); } teamData.clear(); } int interface::addMember(string name, int year){ cout << "Add Team Member" << endl; ++_id_latest; teamData[_id_latest] = new framework(_id_latest); teamData[_id_latest]->setName(name); teamData[_id_latest]->setYear(year); return _id_latest; } void interface::eraseMember(int id){ cout << "Erase Team Member" << endl; delete (teamData[id]); teamData.erase(id); } void interface::showMember(int id){ cout << "Show Team Member" << endl; teamData[id]->getID(); teamData[id]->getName(); teamData[id]->getYear(); }
- とても簡単な使用例
- 本当はキー待ちIOとかファイル出力とかちゃんとやるべき
不敬
#include <iostream> #include <string> #include "libDataBase.hpp" using namespace std; using namespace database; int main(){ cout << "User name and year" << endl; string name; cin >> name; int year; cin >> year; interface mydatabase = interface(name ,year); int Aiko_id = mydatabase.addMember("敬宮愛子", 2001); int Hisahito_id = mydatabase.addMember("秋篠宮悠仁", 2006); mydatabase.showMember(Aiko_id); mydatabase.showMember(Hisahito_id); mydatabase.eraseMember(Aiko_id); mydatabase.eraseMember(Hisahito_id); };
実行
% bin/main User name and year Soluna_Eureka 2000 framework: Default Constructor, ID: -1 interface: Normal Constructor framework: Normal Constructor, ID: 0 framework: Substitute, ID: -1 -> 0 Get ID: 0 Get Name: Get Year: 0 framework: Destructor, ID: 0 Set Name: Soluna_Eureka Set Year: 2000 Add Team Member framework: Normal Constructor, ID: 1 Set Name: Soluna_Eureka Set Year: 2000 Add Team Member framework: Normal Constructor, ID: 2 Set Name: 敬宮愛子 Set Year: 2001 Add Team Member framework: Normal Constructor, ID: 3 Set Name: 秋篠宮悠仁 Set Year: 2006 Show Team Member Get ID: 2 Get Name: 敬宮愛子 Get Year: 2001 Show Team Member Get ID: 3 Get Name: 秋篠宮悠仁 Get Year: 2006 Erase Team Member framework: Destructor, ID: 2 Erase Team Member framework: Destructor, ID: 3 interface: Destructor framework: Destructor, ID: 1 framework: Destructor, ID: 0
見た感じ問題なく動いてるからよし
framework: Default Constructor, ID: -1 interface: Normal Constructor framework: Normal Constructor, ID: 0 framework: Substitute, ID: -1 -> 0 Get ID: 0 Get Name: Get Year: 0 framework: Destructor, ID: 0 Set Name: Soluna_Eureka Set Year: 2000
まずframework
のデフォルトコンストラクタが起爆して,次にinterface
のコンストラクタが起爆する
そして新たにframework
コンストラクタが起爆して,生成されたオブジェクト(右辺値)のコピー代入演算子によるオブジェクト(左辺値としてのメンバ変数)への代入が発生し,最後にデストラクタでオブジェクト(右辺値)が消滅する
Add Team Member framework: Normal Constructor, ID: 1 Set Name: Soluna_Eureka Set Year: 2000 Add Team Member framework: Normal Constructor, ID: 2 Set Name: 敬宮愛子 Set Year: 2001 Add Team Member framework: Normal Constructor, ID: 3 Set Name: 秋篠宮悠仁 Set Year: 2006
ここではコンストラクタのついでにadd
を使ってmap
のnew framework
に登録している
愛子様,悠仁様,両親王陛下も.わたくしと全く同じ動作をされておりますが,全く同じ関数をご使用賜れておられるゆえ,当然のことだろうと存じ上げます(番号の順番はスルーしてくれ)
Erase Team Member framework: Destructor, ID: 2 Erase Team Member framework: Destructor, ID: 3 interface: Destructor framework: Destructor, ID: 1 framework: Destructor, ID: 0
そしてerase
もデストラクタもちゃんと動いている,撃ち漏らしはないように思える
感想
<map>
と<string>
を初めて使った,しかしmap
はともかくstring
は日本語をそのまま使えてしまっているが,文字コード的に本当にコレで良いのかどうか疑問,ぶっちゃけまだ解決しきった問題でもなさそうだけど
あとポインタで持った方がコピーコスト少ないし拡張性あるし結局はポインタを使う方が良いという結論に至った
自作の行列クラスについて
作ってみたけどコピー代入演算子の引数は参照渡しが使えなかった,だって
z = A*x + B*y
とかやる場合だと,operator*
やoperator+
の返り値は行列クラスそのものになるし,これをそのまんまoperator=
で受け取るには値渡ししかない
参照渡しと値渡しそれぞれのコピー代入演算子は,クラスで実装できてもコードで呼び出すときは曖昧さでエラーを吐くので,ユーザーは使い分けもできない
引数のconst
の厳密な管理も辛い,しかしこっちはちゃんとやるべきだった,マシなコーディング規約を考えて修正したいとは思ってる
分割コンパイルと静的ライブラリでチェーン実装のメモ(c++)
注意事項
- 2週間くらい1人で適当に弄った結果,アテにならん
- 静的リンクだけやってる
- 動的リンクはやらない
- 動的ロードもやらない
- あくまで過酷環境の組込実装のためにやってる
- 動的リンクも動的ロードも無理らしい…
- 暇があれば後でやってみようと思ってる
- スマートポインタは使わない
c++98
を想定してる- スマートポインタは
c++11
以降の機能なので
概要
- プロジェクトの構成
- 認識のしやすさ
- 管理のしやすさ
- モジュールとして分割して開発
makefile
の改造- シェルの文法なのか
make
の文法なのかこれもうわかんねぇな make
の機能だけで色々やろうとしたが個人的には無理だった- 文法の縛りがキツくて関数の実用性をあまり感じなかった
- もうシェルスクリプトを叩く方が早い?初期化もシェルで叩く?
- モジュール追加時の自動構成もできると気持ちが良さそう
- シェルの文法なのか
- クラス
x
をメンバ変数に持つクラスy
を定義するヘッダファイルでは,前方宣言でx
を定義してinclude
の多重ロードを防止する- つまりヘッダファイルでは
x
を定義してinclude
せずに,ソースファイルではx
を定義するヘッダファイルをinclude
する - たとえば「「「クラス
x
」を抱えるクラスy
」を抱えるクラスz
」を定義する時にヘッダファイルに依存関係を全て表示すると,y
を定義するヘッダファイルに加えてy
を経由してx
を定義するヘッダファイルまでロードする,といった多重インクルードが発生する- 何レイヤも下にあるライブラリのヘッダファイルを遡上するのは,まともにやろうとしても
include path
の管理が壊れる - 名前空間の管理も高レベルになる,ヘッダファイルの挙動によっては思わぬ動作を生むかもしれない,俺は辛い耐えられない
- 1つのライブラリを複数のソースで構成して
include
するならOK,ただしそれでもinclude path
には十分に注意する
- 何レイヤも下にあるライブラリのヘッダファイルを遡上するのは,まともにやろうとしても
- つまりヘッダファイルでは
- そのような実装をするクラス
y
はクラスx
をメンバ変数に持つが,コンパイラの都合によって実体ではなくポインタで持つ必要があり- たとえば「「「クラス
x
」を抱えるクラスy
」を抱えるクラスz
」の実装をコンパイルする際は,多重インクルードが阻止されているためにx
のサイズが不定となり,つまりy
のサイズも不定となり,そしてx
のサイズも不定となるため,普通にエラーを吐かれる- もっと簡単に表現すると2つ下のレイヤで定義されたクラスのサイズがわからないから実体を持つとコンパイルできない
- 実体を持たずにポインタを持つならば,ポインタのサイズは処理系によれどいずれも既知となるはずなので,この問題は起こらない
- ある意味でシンボル指向に基づくオブジェクト指向みたいな感覚を覚えた,普段からリンクする時のことを考えた方が良い
- ユニークポインタが未実装の
c++98
ではnew
とdelete
を自力で管理しないとメモリリークするので,気をつけるべし - さらに言えばヘッダファイルが隔離されているせいで2つ下レイヤのクラスにあるメソッドや変数は叩けない,気をつけるべし
- たとえば「「「クラス
- 静的ライブラリの取り扱いについて
- 実行ファイルの生成時にシンボルの多重定義を防止するには,ライブラリ毎に生成した静的ライブラリを最後にまとめて呼び出す(普通はこうするっぽい)か,依存する静的ライブラルをバラして得たオブジェクトとコンパイルで得たオブジェクトをまとめてマージして重複を消し飛ばす作業を,ライブラリ毎の静的ライブラリの生成からメインのオブジェクトが呼ぶ静的ライブラリの生成まで一貫してやり続けるか…
- 前者はライブラリの依存関係がソースファイルの
include
の依存関係に一致しないので.リンカに渡すパスの管理が面倒 - 後者はディスクを無駄に消費する,ライブラリ毎に設定されるべき責任境界が壊れる,実装の変更の反映も簡単にできない
- 前者はライブラリの依存関係がソースファイルの
- ちなみに過酷環境の組込実装では浮動小数点演算が
run-time-support
のライブラリとして提供されている場合があるらしい
- 実行ファイルの生成時にシンボルの多重定義を防止するには,ライブラリ毎に生成した静的ライブラリを最後にまとめて呼び出す(普通はこうするっぽい)か,依存する静的ライブラルをバラして得たオブジェクトとコンパイルで得たオブジェクトをまとめてマージして重複を消し飛ばす作業を,ライブラリ毎の静的ライブラリの生成からメインのオブジェクトが呼ぶ静的ライブラリの生成まで一貫してやり続けるか…
プロジェクトの構成
以下は一例
. ├── bin │ └── main ├── libA │ ├── inc │ │ └── libA.hpp │ ├── lib │ │ └── liblibA.a │ └── src │ ├── clsA.cpp │ └── clsA.hpp ├── libB │ ├── inc │ │ └── libB.hpp │ ├── lib │ │ └── liblibB.a │ └── src │ ├── clsB.cpp │ └── clsB.hpp ├── makefile └── src └── main.cpp
規則
- モジュール毎にディレクトリを設置
- その下は
inc
とlib
とsrc
に分割 src
にはソースファイルとヘッダファイルを設置lib
には静的ライブラリを設置inc
のはsrc
にあるヘッダファイルを全てinclude
した代表ヘッダファイルを設置- 他モジュールやメインモジュールから参照されるヘッダファイルはこの1つに統一する
- モジュールのコンパイル時にソースファイルに応じて
makefile
で自動的に生成する - ただし
include path
はsrc
の方にも通す,代表ヘッダファイルが呼び出すので
makefile
の改造
以下は一例
SHELL = /bin/zsh MAIN_SRC = src/main.cpp MAIN_OBJ = main.o MAIN_BIN = bin/main GCC_BIN = clang++ GCC_FLG = -std=c++98 -O2 make : make libA make libB make main libA : FORCE $(call setAllVar,libA) $(GCC_BIN) $(GCC_FLG) -c $(libA_SRC) @ar cr $(libA_OBJ) *.o @rm -f *.o $(shell echo '#pragma once' > $(libA_REP);) $(foreach hed,$(libA_HED),$(shell echo '#include "$(hed)"' >> $(libA_REP);)) libB : FORCE $(call setAllVar,libB) $(call setRefVar,libA) $(GCC_BIN) $(GCC_FLG) -c $(libA_INC) $(libB_SRC) @ar x $(libA_OBJ) @ar cr $(libB_OBJ) *.o @rm -f *.o __.* $(shell echo '#pragma once' > $(libB_REP);) $(foreach hed,$(libB_HED),$(shell echo '#include "$(hed)"' >> $(libB_REP);)) main : FORCE $(call setRefVar,libB) $(GCC_BIN) $(GCC_FLG) -c $(libB_INC) $(MAIN_SRC) @ar x $(libB_OBJ) $(GCC_BIN) $(GCC_FLG) -o $(MAIN_BIN) $(MAIN_OBJ) $(libB_LIB) @rm -f *.o __.* FORCE : define setAllVar $(eval $1_HED = $(notdir $(wildcard $1/src/*.hpp))) $(eval $1_SRC = $(wildcard $1/src/*.cpp)) $(eval $1_OBJ = $1/lib/lib$1.a) $(eval $1_REP = $1/inc/$1.hpp) $(eval $1_INC = -I$1/inc -I$1/src) $(eval $1_LIB = -L$1/lib -l$1) endef define setRefVar $(eval $1_OBJ = $1/lib/lib$1.a) $(eval $1_REF = $1/inc/$1.hpp) $(eval $1_INC = -I$1/inc -I$1/src) $(eval $1_LIB = -L$1/lib -l$1) endef
説明
shell
はzsh
を指定する- ただしシェルスクリプトの中で
make
の機能を叩くのは無理- 関数を書いても実用性を感じられなかったのはこのため
- せいぜい変数を自動設定するくらいしかできなさそう…
- 存在するソースに応じ代表ヘッダファイルを自動的に生成する
$(foreach)
して$(shell echo)
したらできた…
- 空変数
FORCE
を使って常に強制的にmake
を実行させる- モジュール毎にコンパイルするだけなら十分だと思った
-L
は静的ライブラリの探索パスでリンク時に使う,-I
はヘッダファイルの探索パスでコンパイル時に使う- リンク時に
-L
と一緒に使う-l
はライブラリを指定するが(lib)libA.a
みたいに先頭のlib
が削れるらしい
静的ライブラリの取り扱い
バラしてマージするパターンを実装した
ar x libName
でバラしてar cr libName *.o
でマージしてrm *.o __.*
で一時ファイルを消す
macOSに入るcommand line tool
のclang++
は__.SYMDEF
なるものを出すが,たぶんgnu
のclang++
でも同じことが起きる
見た感じシンボルのリスト(が短縮されたもの)が入ってるテキストファイルっぽかったが,用途がよくわからない,調べる必要があるかも
多重ロード防止実装
以下は一例
libA
#pragma once namespace libA{ class clsA{ private: int __Num; protected: int _Num; public: clsA(); ~clsA(); int Num; void aLog(); }; }
- 3レベルそれぞれのアクセス権限に1つずつ変数を置く
#include <iostream> #include "clsA.hpp" using namespace std; using namespace libA; clsA::clsA(){ cout << "construct A" << endl; __Num = 0; _Num = 1; Num = 2; } clsA::~clsA(){ cout << "destruct A" << endl; } void clsA::aLog(){ cout << " log from A" << endl; cout << " private num: " << __Num << endl; cout << " protected num: " << _Num << endl; cout << " public num: " << Num << endl; }
libB
#pragma once namespace libA{ class clsA; } namespace libB{ class clsB{ private: libA::clsA* __aObj; protected: libA::clsA* _aObj; public: libA::clsA* aObj; clsB(); ~clsB(); void bLog(); }; }
- 1つ下のレイヤのクラスをポインタで持つ
- 3レベルそれぞれのアクセス権限に1つずつ置く
#include <iostream> #include "libA.hpp" #include "clsB.hpp" using namespace std; using namespace libA; using namespace libB; clsB::clsB(){ cout << "construct B" << endl; __aObj = new clsA(); _aObj = new clsA(); aObj = new clsA(); } clsB::~clsB(){ cout << "destruct B" << endl; delete __aObj; delete _aObj; delete aObj; } void clsB::bLog(){ cout << "log from B" << endl; cout << " private A:" << endl; __aObj->aLog(); cout << " protected A:" << endl; _aObj->aLog(); cout << " public A:" << endl; aObj->aLog(); }
new
とdelete
でメモリを管理するc++11
以降はユニークポインタを使うべき?
- ポインタで持つので
.
ではなく->
で変数やメソッドを呼び出す
main
#include <iostream> #include "libB.hpp" using namespace std; using namespace libB; int main(){ clsB* bObj = new clsB();; bObj->bLog(); delete bObj; };
- クラス設計するわけでもないので,オブジェクトは直置きできるはず
- が,
new
とdelete
を使った方がコーディング規約的に楽かも
- が,
main
からは2つ下のレイヤのclsA
の変数とメソッドを叩けない- ある意味では意図せずにセキュアな実装になっててビックリした
実行
% make make libA clang++ -std=c++98 -O2 -c libA/src/clsA.cpp make libB clang++ -std=c++98 -O2 -c -IlibA/inc -IlibA/src libB/src/clsB.cpp make main clang++ -std=c++98 -O2 -c -IlibB/inc -IlibB/src src/main.cpp clang++ -std=c++98 -O2 -o bin/main main.o -LlibB/lib -llibB % bin/main construct B construct A construct A construct A log from B private A: log from A private num: 0 protected num: 1 public num: 2 protected A: log from A private num: 0 protected num: 1 public num: 2 public A: log from A private num: 0 protected num: 1 public num: 2 destruct B destruct A destruct A destruct A
大域変数に自作クラスを使うと…
過酷環境の組込実装では初期化と割込制御をint main(){}
とinterrupt void xxx(){}
のように実装するので,例えば制御器は大域変数を介して動作する必要がある
それぞれの関数や変数は別のソースに書いてextern
してまとめてコンパイルすれば使えるが,そういうやり方ではいつか地獄を見るのでよろしくないとされる
では大域変数として制御器の機能を持つ自作クラスをオブジェクト化するとどうなるのかと言えば,(処理系によって違うが)main
の実行前の処理にオブジェクトのコンストラクタが走るように登録されたり,実行後の処理にデストラクタが走るように登録されたりする,これもrun time support
のライブラリで提供される機能となる
しかし実質的にmain
が初期化ルーチンとなっているなら,クラスのポインタを大域変数に置きつつ後でnew
で実体化しても良さそうなのだが,するとdelete
ってどうやって登録すれば良いのだろうか…?