TeX Liveユーティリティのリポジトリにあるtabularray.styが使えないので注意

旧版の2023Aではエラー吐きます 以下からローカルに落とす必要があります

github.com

コンパイルする.texファイルと同じ場所に置いとけばパスが優先されます

原因?

もしかしたらexpl3.styの最新の仕様とミスマッチしている可能性がある
変数宣言あたりで何か改訂があったかもしれない
昨年7月までは普通に動いていたので…

Ventura 13.6.1のアップデートでMBPが壊れた

環境

13.6.1とは

  • 2023年10月25日発
  • セキュリティパッチ

support.apple.com

support.apple.com

事象

  • 寝ている間にアプデをかけた
  • 翌朝に確認したら死んでいた

症状

通常の起動

  • まずリンゴマークが点灯する
  • USB-Cを経由してネットワークに繋ぐと文鎮化する
  • 繋げないとリンゴマークが出現してから文鎮化する
  • 外付けキーボードorマウスはアクティブ状態である

セーフブート

  • 起動オプション通知は出るがその直後に文鎮化する

対応

  • 症状としては「ファームウェア破損」が該当した
  • まずは別のMacからファームウェアを復活させる
    • 同時にOSのアップデートを走らせて,その最中にファームウェアが死んだ場合,OSはインストールし直す必要がある
    • OSはメインディスクに入っており,APFSコンテナ内でユーザーデータと共用する,ファームウェアは別の場所にある
  • もしダメなら全データを削除して導入をやり直す
    • つまりファームウェアを入れた上でディスクを仕切り直していちからOSを入れてユーザー登録をしなければならない
  • それもダメなら基盤が死んでいるので修理が必要
  • 現状で配布されている最新のOSはSonomaなので,OSの再インストールを行うと(Time Machineでバックアップを取っていない限り)強制的にVenturaからSonomaに変更される
    • ただGenius Barでスタッフ相手に粘るともしかしたらVenturaを落とせるかも?まぁSonomaも安定しているようだけど

生きているApple SiliconのMacを持っている場合

  1. 生きている方にApple Configurtatorを導入する
  2. 以下の手順書に基づいて作業する(かなり面倒)
  3. OSを再インストールする(データは飛ばない)

持ってない場合

  • 諦めてGenius Barを予約して良きに計らってもらう,ファームウェアの復活は自分のMacを渡してから20分弱で終わる
  • OSの再インストールは時間がかかるため持ち帰ってやる,もし失敗したらディスクが飛んでいることになるので修理

macOS Sonomaの使い心地

  • デスクトップにウィジェットを固定できるのは良い
  • スクリーンセーバーは訳わからん…要らないと思う
  • それ以外はほぼVenturaと同じなので違和感はない
  • ぶっちゃけFCPXとLogicのアプデの方が遥かに大事だし,あとM3系列はメディアエンジンがAV1に対応したのがデカい,3nm化でSoC自体の性能もちゃんと上がったように見えるし強そう

IEEEtran.clsのConferenceの使い方

IEEEtran.clsを使ってConferenceを投稿したので備忘録

環境

  • M1 Mac Ventura
  • MacTeX 2023
    • homebrewで入手

テンプレート入手

ieee.orgの配布ページ,https://www.ieee.org/conferences/publishing/templates.html LaTeX TemplateBibteX 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.clsciteIEEEtran.bstのコンビでは,一箇所に複数の文献を配置するとそのレイアウトが圧縮されない,citeの仕様としては圧縮されるべきところIEEEtran.clsと一緒に使うと圧縮禁止オプションもなしに強制的にそうなってしまう,なぜならそれがIEEEのスタンダードスタイルらしいので 要するに[1][2][3][5]を[1-3,5]としたい時はciteではなくnatbibIEEEtranN.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. 以下のコースを選り好む(シールドブレイクのパターンは報酬がカスなので確実に回避)
    • 1面タイマン(回転仕切コースにおいて以下の構成ではエイリアンが天敵なので要注意)
    • 2面タイマンの通路コース(仕切コースはボーナス稼働時にモブが移動できないので×)
    • BOSS(できれば舟形が良い,これがレンガもコインもカードも最もよく稼げると思う)
  2. ビック・パーサ(ボーナス稼働時にDPS◎)
  3. Nexus(ボーナス稼働時にDPS◎,砲台化)
  4. 攻撃型カード(ノーミー,クマ,ソルジャー,騎士)(ボーナス稼働時にDPS◎)
  5. 30秒広告で連射2倍(ビック・パーサでのクリアに必須かも)
  6. ボーナス稼働でコインとカードをゴリゴリ稼ぐ(これがミソ)

追記(6/14)

アプデで対策されたらしく草 クリア時の通信が強制的に遮断されるようになりMac周回の旨みが死んだ ただ現状ではまだ高級ルーレットが回せる強みがあるので物事は考えよう

C++ 継承のprivate / protected / publicの切り替え

アクセス権限の意味

アクセス指定子をつけると設定できる

private

該当する関数または変数には,それらが実装されたクラス(のメソッド)のみアクセスできる

protected

該当する関数または変数には,それらが実装されたクラス(のメソッド)とそのクラスから派生したクラス(のメソッド)のみアクセスできる

public

制限はない,オブジェクトを無から生成して渡すためにpublicの関数を経由する必要がある

アクセス権限の規制

継承時にアクセス指定子をつけると継承元の関数または変数のアクセス権限にリミットがかかる 隠蔽する方向にのみ動作する

private

publicprotectedprivateになる

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

publicprotectedになる

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
}

使い道

部分的にprotectedprivateにしたいです,特に組込実装ではコード領域すらも節約する必要があるかと思います

一括で降格させる方法

つまり継承時にアクセス権限の規制をやれということで

多重継承

名前衝突や菱形問題の原因になるのでやめたい もしくはせめて仮想継承をちゃんと実装したい

名前衝突

衝突しなかったらスコープ無しで派生クラスから呼べる 衝突したらスコープ派生元クラスを解決する必要がある もし衝突してもエイリアスの作成は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個しかできないということ むしろ今までのケースが「衝突しなかったらスコープ無しで派生クラスから呼べる」のを悪用して同名の再定義をしている

菱形問題

これらの例はABにそれぞれ独立にxがあった上でCに合体させたせいで詰んでいるが,xを持つAからBCに派生させた上でDに合体させるような場合,(BCが悪さをしない限り)仮想継承で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::xC::xD::xも指すが,アクセス権限はA::xB::xC::xD::xでそれぞれ設定され,かつusingエイリアスを作成するとその都度に設定が上書きされる(これでも実体は1つなので問題ない) 最終的にDpublic: 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がいくつかあり,パーティションとして仮想イメージのdisk5disk6がそれらを指している
おそらく問題が起こるのはその下のdisk5s1disk6s1だが,こいつにfirst aidしても上記のメッセージが出て解決しない,チェックサムが壊れたのかその仕様が変更されたのか…
そこでdisk5disk6にfirst aidするとdisk5s1disk6s1が破損していると認識して修復しようとしてくれる,あとは復活するのをお祈りする,そこそこ時間がかかるので注意

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 Progithub.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

まとめ

  • macOSDarwinのバージョン変更は現在ではリンクしている
  • Darwinのバージョンが同じでもチップ依存でXNUの型番が違う場合がある
  • T8000番台はモバイルまたはノート向け,T6000番台はラップトップ向け,だと思われる
    • 通常のM1やM2は,ラップトップであってもT8000番台が振られる
    • M1 ProとM1 Max(とM1 Ultra?),M2 ProとM2 Max(とM2 Ultra?)は,搭載する機種がノートであってもT6000番台が振られる
      • つまりProチップやMaxチップでなければ「真のMac」とは言えない可能性が微レ存…?
  • ただしintel macI386X86_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年発売モデルが中古化して認定整備済品が値下げした今,IntelMacからApple Silicon系Macに乗り換えるべきである.
さらに,Venturaも最初はバグまみれだったが,そろそろメーカーもM1かつVenturaに対応するリリースを出す頃合いだろう.

ちなみに,MacBook Pro (15-inch, 2016)はまだビンテージ品の扱いなので,やろうと思えばまだ修理は可能…かもしれない

support.apple.com

ぶっちゃけ,またキーボードが壊れたし,OSやAppが高機能化しているせいでファンがよく回るし,常用するには辛すぎた

選定スキーム

個人的な要件

  • 画面 15inch以上
    • 長時間の利用なら大画面の方が良いし,レイアウトの関係でスピーカーの音質も良いし,筐体やファンの放熱量も高いし,チップの性能も上が選べるし…
  • RAM 32GB以上
    • 16GBで5年ほど使った結果,オンライン会議をしながら必要の資料を随時に引っ張り出すには少し足りなかったし,趣味の創作なんてもってのほかだった
  • SSD 512GB以上
    • 256GBで5年ほど使った結果,SSD寿命を20%ほど消費した,あと趣味のツールとかを入れると普通に足りなかった,仕事用PCはSSD1TBを支給されてる
  • GPU 強め
    • 調べ物(ブラウジング)が爆速になるので生産性が上がる,もういくらGPUがあっても損をすることはなさそう,これからはAppがGPUを浪費する時代か
  • 以上を満たすスペックのうち投資効率が良さそうなもの
    • 認定整備済品の価格設定を吟味する必要がある!
  • できればUS配列…だが認定整備済品は無理かもしれない
    • まぁ普段は安い外付けのキーボードを使い捨てればOK

まずこのページを見ろ

Macの認定整備済品リストから要件を満たすものを列挙させている

https://www.apple.com/jp/shop/refurbished/mac/1tb-2tb-4tb-512gb-8tb-2021-16%E3%82%A4%E3%83%B3%E3%83%81-macbook-pro-32gb-64gb

取り敢えず価格の安い順で確認だ

どういう戦略で選んだのか?

M1 ProとM1 MaxではCPUのCore数は全く同じなので比較しない
ただ「M1 ProかM1 Maxか」と「SSDの容量」「RAMの容量」と「SSDの速度」「RAMの速度」に関係があるらしい(俺はLPDDR5-6400というだけで満足)

最安モデルは318800円

www.apple.com

GPU 16Core,RAM 32GB,SSD 512GB
ちなみにこの記事を書いた時点でシルバーは売り切れている

次に来るのは339800円…だが,

こっちはGPU 24Core,RAM 32GB,SSD 512GB

www.apple.com

こっちはGPU 16Core,RAM 32GB,SSD 1TB

www.apple.com

つまりどちらかを選んでねということである

その次に来るのは354800円

www.apple.com

GPU 32Core,RAM 32GB,SSD 1TB

そして問題なのがその次の360800円

これはGPU 24Core,RAM 32GB,SSD 1TB

www.apple.com

これはGPU 32Core,RAM 32GB,SSD 512GB

www.apple.com

…俺の読み間違えでなければ,性能と価格の順序がここで崩壊していることになる…なぜ?

ちなみに次は380800円なのだが,

これはGPU 24Core,RAM 64GB,SSD 512GB www.apple.com

これはGPU 16Core,RAM 32GB,SSD 2TB

www.apple.com

となっており,そしてここから先のGPU 32Coreを持つオプションは一気に値上がりする,いずれもSSDとRAMの増設の影響をモロに受けてるのかもしれない

最終的に購入したオプション

つまり,これ

www.apple.com

が良いと判断した

my new gear...

考察

いやまったくわからん,どうしてこうなってるのかなにも想像がつかん
バグかと思ったが頼んで届いたらまんまのスペックだったし夢でもない
SoCだし普通に考えればチップまでイジる再整備なんてあるわけがない
だからパーツの価格がどうこうなんてロジックは存在しないはずだろう
ともかく性能が上位互換で尚且つ安価なら明らかに買うべき品物である

使用している感想

MBP2016と比較して

  • 音質良し,画質良し,見た目良し,ハードウェア的には完全な上位互換
    • でも排気口周りの構造が少し気になったかも
      • まぁほとんどファンが回らんから問題なし
    • あとキーボードが周り真っ黒で見づらいかも
      • 交換のしやすさ考えたらしょうがないかも
      • MBP2016はマジで修理の敵だったらしいし
  • 書類仕事なら何やっても熱くならない
  • OBSを回してもほぼファンが回らない
  • MainStageが設定を盛っても楽に動く
  • Clip Studio Paintの描画が爆速で動く
  • ChromeSafariもEdgeも爆速で動く
    • ブラウザの表示が遅いのはネットワークではなくGPUとメモリのトロさのせいだったらしいことに気づいた
  • Venturaのバグ?不安定動作?がまだあるっぽい
  • JIS配列クソ使いづらい,コーディングに不向き
  • ベゼルが薄型化していて良いM字ハゲだろ
  • やっぱTouch Barなんて要らんかったんや!
  • MagSafeに電力系を分けたのは英断だと思うよ
    • でもUSB-Cが4つの方が好き,SDXCいらん
  • HDMIポート標準装備は評価できる

でも認定整備済品って中古だろ?

そうらしいが,取り敢えず届いてすぐにhomebrewsmartctlを最速で導入して,そのまま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"が展開された上で翻訳単位としてhogemainの2つが発生して,それぞれが処理されてシンボルテーブルを持つオブジェクトとして出てくる main側はHoge周りの実装をしていないため,hoge.hppの宣言した内容に期待して,名前空間Hogeを含めたシンボルをあらかじめ設定する ゆえにhoge側は名前空間Hogeを含めたシンボルを実体に持つような実装が期待されており,そうでなければリンク時に名前を解決できず失敗する

ソース側でusing namespace Hogeを利用して(名前空間の解決の記法を省略して)実装することは不可能であり,それはコンパイラ名前空間の探索と適切な配置よりも先に勝手に大域的な配置をしてしまうためである… なんでそうなのかは知りませんが,歴史的な経緯とかあるのでしょうか?やっぱり外部リンケージがデフォルトな言語だからなんですかね,externconstも難解すぎてこのままでは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;しつつクソデカネストを使わないなら,多分これが一番楽だと思います また宣言されたtypedefclassの名前スコープを無視できる,人によっては折衷案に見える?

#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についての学習からやっている

何をやっているの?

atcoder.jp

これのEX22までやって肩慣らしを始めたつもりでいる

…結論から言うと「やべぇアルゴリズムなんもわからんかも」という状態であり,そもそも組込実装とは要求されている要件が違いすぎるためである

atcoder.jp

atcoder.jp

クラス設計にそういう要素を組み込むのは確かに面白いと感じたが,それ以上に問題がハードになっていくにつれてこの作業に命を賭けるのは正直キツいよなぁと思った

自作クラスを実体としてメンバ変数に持つ時の挙動

概要

前回のあらすじ

soluna-eureka.hatenablog.com

分割コンパイルでモジュール開発をする場合は,ライブラリのシンボル指向実装とinclude地獄の回避可能なヘッダ記述手法の都合から,モジュールが異なる(レイヤが1つ下の)自作クラスをメンバ変数に持つなら,実体ではなくポインタで持った方が良く,かつそれらはnewdeleteを使用してメモリ管理をちゃんと実装し,コンストラクタもデストラクタも確認するべし,的な結論に至った(c++98縛りゆえスマートポインタ不使用)

しかしモジュールの開発においては,同一レイヤのレベルで自作クラスを実装して,そのまま自作クラスのメンバ変数に組み込む,みたいな場合があるかもしれない(,むしろそれが基本的なクラス設計のやり方なのでは…?)

今回のあらすじ

  • 同じモジュール内で実装したクラスでも,ポインタで持つ方が安心できる
    • 面倒でもオブジェクトはnewdeleteする癖をつけた方が良いかもしれない
    • そもそもそういうやり方を統一した方が混乱しない,組み込み型じゃないなら尚更
  • 実体で持つ場合は必ず=の定義を確認するべし
    • 例えば,「「クラスx」を抱えるクラスy」をオブジェクトにする場合,クラスyのコンストラクタが走る前にクラスxのデフォルトコンストラクタが走る,自作クラスを実体で持つ限りこの挙動は避けられない
    • クラスyのコンストラクタ内でクラスxのコンストラクタを叩き,その結果をメンバ変数に=で接続すると,クラスの初期化と同様に反映できる気もするが,これは初期化の文法ではなく代入の文法として動作する
      • なぜなら実体で持つメンバ変数はコンストラクタを叩く前にデフォルトコンストラクタが走って既に実体化したから,その動作はコンストラクタで得たオブジェクトをオブジェクトに=で代入することを意味する
      • もちろんコピーコンストラクタでもない,例えコピーを名乗ってもコンストラクタは2度と走らない
    • つまり演算子=が曖昧な自作クラスを実体としてメンバ変数に持つと予期せぬ動作が起きるかも…
      • 自作行列ライブラリ(公開できない)の開発中にコレに遭遇して,初めてちゃんと仕様を確認した…
    • 1つ下のレイヤのオブジェクトのメンバ変数を叩いて加工するよりも,最初からそのオブジェクトに固有の演算子=を定義した方が,実装の責任境界が明確だし後で他のクラスに使われる時も好都合だしで生産性がある

コピー代入演算子のみを扱う,ムーブ代入演算子は扱わない ただし速度向上を考えたら後者の方が良いのかもしれない…

演算子=オーバーロードが必要なケース

  • 他のクラスに実体で持つ予定のクラス
    • そうじゃないなら要らないかも…
  • 算術演算みたいな使い方をするクラス
    • 行列クラスとかそもそも不可避…
    • 引数の設定
  • メンバ変数にポインタを持つクラス
    • newdeleteまたはmallocfreeでメモリ確保したポインタを持ってその中でデータを保持している場合,ポインタをコピーするのかポインタの中身をコピーするのかちゃんと考えて実装しよう

実装

.
├── 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をつけておく
    • 戻り値をvoidではなくclassName&にすると=を連鎖的に実装できる
      • a=b=cを実装するには構文の都合で常に同じクラスを返さないとダメ
      • でも今までその書き方をしたことがない,混乱の元だと思っているので
  • メンバ関数に非破壊宣言の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自体はintidframework*なデータを持つteamDataに使用
    • addMembereraseMembershowMemberAPIみたいにして実装
  • _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を使ってmapnew 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ではnewdeleteを自力で管理しないとメモリリークするので,気をつけるべし
    • さらに言えばヘッダファイルが隔離されているせいで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

規則

  1. モジュール毎にディレクトリを設置
  2. その下はinclibsrcに分割
  3. srcにはソースファイルとヘッダファイルを設置
  4. libには静的ライブラリを設置
  5. incのはsrcにあるヘッダファイルを全てincludeした代表ヘッダファイルを設置
    • 他モジュールやメインモジュールから参照されるヘッダファイルはこの1つに統一する
    • モジュールのコンパイル時にソースファイルに応じてmakefileで自動的に生成する
    • ただしinclude pathsrcの方にも通す,代表ヘッダファイルが呼び出すので

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

説明

  • shellzshを指定する
  • ただしシェルスクリプトの中で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 toolclang++__.SYMDEFなるものを出すが,たぶんgnuclang++でも同じことが起きる 見た感じシンボルのリスト(が短縮されたもの)が入ってるテキストファイルっぽかったが,用途がよくわからない,調べる必要があるかも

多重ロード防止実装

以下は一例

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();
}
  • newdeleteでメモリを管理する
    • c++11以降はユニークポインタを使うべき?
  • ポインタで持つので.ではなく->で変数やメソッドを呼び出す

main

#include <iostream>
#include "libB.hpp"

using namespace std;
using namespace libB;

int main(){
    clsB* bObj = new clsB();;
    bObj->bLog();
    delete bObj;
};
  • クラス設計するわけでもないので,オブジェクトは直置きできるはず
    • が,newdeleteを使った方がコーディング規約的に楽かも
  • 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ってどうやって登録すれば良いのだろうか…?

(小ネタ)知らん間にmacOSでフルスクリーン時にメニューバーを常時表示できるようになっとる

やりかた

macOS Montereyならみんなできるはず?

  1. システム環境設定
  2. Dockとメニューバー
  3. フルスクリーンでメニューバーを自動的に表示/非表示

こんな項目あったっけ…

その後

Dockのサイズが規定値内に戻されるので

defaults write com.apple.dock largesize -int 256 | killall Dock

しよう

qiita.com

MacBook Pro 2016 Late終了のお知らせ

ついにVenturaからこの世代がサポートを打ち切られた 2018年に有楽町ビックカメラで10万くらい安く売られてたのを現物で買ったUSキー持ちの正規品 ディスプレイもスピーカーも元気だけどキーボードは完全に壊れちゃったので外付けを使っている 最近は起動が遅くディスクもヤバい,次どうしようか,M2 Airは為替がアレだしMacSSD高い… (intelチップ2016 Late世代でもtouch barつきモデルはSSDの換装はできないらしいので)