クラスのオプションと自作

\documentclass[unknownoption]{article} と書いてもコンパイルは通ります。クラスのオプションは綴りを間違えてもエラーにならず、LaTeX はログの奥に LaTeX Warning: Unused global option(s): と書き残すだけ——PDF が出てしまうので、まず気づきません。ところが \usepackage[unknownoption]{color} のほうは即座に停止します。この非対称は仕様の気まぐれではなく、クラスとパッケージでオプションの既定の行き先が違うという設計から来ています。このページでは、その仕組みを入口にして、\DeclareOption\ProcessOptions、新しい \DeclareKeys、そして \LoadClass で既存クラスの上に自分のクラスを建てるところまでを組み立てます。

なぜ綴りを間違えたクラスオプションは止まらないのか

答えは公式の手引き clsguide に書かれています。クラスファイルに \DeclareOption* が無い場合、宣言されていないオプションは黙ってすべてのパッケージへ渡されます。パッケージファイルに \DeclareOption* が無い場合は、宣言されていないオプションごとにエラーになります。 つまりクラスのオプションは「まだ誰かが使うかもしれない」ものとして持ち回され、最後まで誰も使わなかったときに \begin{document} の時点でこう報告されるだけです——LaTeX Warning: Unused global option(s): に続けて、使われなかったオプション名が角括弧で並びます。一方パッケージのオプションは行き先がそこしかないので、知らない名前は即 ! LaTeX Error: Unknown option 'unknownoption' for package 'color'. になります。

この設計は理にかなっています——\documentclass[dvipsnames]{article} のように、クラス自身は知らないがあとで読み込むパッケージが受け取るオプション(グローバルオプション)は日常的に使われるからです。代償として、タイプミスは沈黙します。 だから実務では二つ習慣を作っておくと得をします。一つは、ビルドのたびにログを Unused global option で検索すること。もう一つは、プリアンブルの先頭に \listfiles を置いて、読み込まれたファイルとバージョンの一覧をログ末尾に出させることです。なお、オプションのコード中で \OptionNotUsed を呼ぶと、そのオプションを明示的に「未使用」の一覧へ送れます。

クラス(.cls)とパッケージ(.sty)の違い

clsguide が示す判断基準は一行です——その機能がどんな文書クラスとも一緒に使えるならパッケージにし、そうでなければクラスにする。 クラスは文書の種類そのものを定義し、\documentclass一つだけ 読み込みます。パッケージは \usepackageいくつでも 重ねられ、文書の種類によらず機能を足します。clsguide が挙げる例が分かりやすい——ある会社が自社の便箋で手紙を組むために作る ownlet は、letter クラスの上に建つがほかのクラスでは使えないので ownlet.cls。図を取り込む graphics はどのクラスでも使えるので graphics.sty、というわけです。

クラスにも二種類あります。articlereportletter のように 自立している ものと、既存クラスの拡張・変種であるもの——clsguide は後者の例として、article の上に建つ proc を挙げています。実際に自分で書くクラスは、ほぼ確実に後者になります。ゼロから版面を組むのは割に合わないからです。書く中身の作法は .cls.sty でほとんど共通で、命令が Class 用と Package 用に対で用意されているだけです(\ProvidesClass\ProvidesPackage\LoadClass\RequirePackage\PassOptionsToClass\PassOptionsToPackage)。

自作クラスが受け取るべき標準オプション

利用者は自作クラスにも標準クラスと同じ感覚でオプションを渡してきます。最低限そろえたい顔ぶれは、本文の基準サイズを選ぶ 10pt / 11pt / 12pt、用紙の a4paper / letterpaper、段組の onecolumn / twocolumn、面付けの oneside / twoside、そして下書き表示の draft(行あふれを黒い印で示す。対は final)。ただし、これらを自前で実装する必要はありません。後で見るように、土台クラスへ そのまま転送する のが定石です。

オプション意味既定
10pt / 11pt / 12pt本文の基準文字サイズ10pt
a4paper / letterpaper用紙サイズ(b5paperlegalpaper なども)letterpaper
onecolumn / twocolumn1 段組/2 段組onecolumn
oneside / twoside片面/両面のレイアウトonesidebook のみ twoside
draft / finalあふれた行を黒い印で示すかどうかfinal

利用者が何も指定しなかったときの既定値をクラス側で決めたいなら、\ProcessOptions\ExecuteOptions{a4paper,11pt} と書きます。これは「あらかじめこのオプションのコードを走らせておく」という宣言で、clsguide も既定の設計を与える用途としてこの形を挙げています。これらのオプションを文書の \documentclass[...] 側で 使う 話や、book 特有の openright などは /learn/document-basics/class-preamble の担当です。ここから先は、受け取る側のクラスをどう書くかに集中します。

ファイルの先頭で名乗る — \NeedsTeXFormat と \ProvidesClass

クラスファイル(myclass.cls)の最初の二行はほぼ定型です。まず \NeedsTeXFormat{LaTeX2e} で、このファイルが LaTeX2e 用であることを宣言します。続いて \ProvidesClass{myclass}[2026/01/01 v1.0 My example class] で、クラス名・リリース日・バージョン・説明を名乗ります。この一行が効いてくるのはログで、コンパイルすると Document Class: myclass 2026/01/01 v1.0 My example class という行が残ります。共著者から「うまく組めない」と言われたとき、まずこの行を見せてもらえば、相手が古い .cls を掴んでいないかがすぐ分かります。

角括弧の中は省略できますが、書いておくと 日付(YYYY/MM/DD 形式) をもとに利用者が \documentclass{myclass}[2026/01/01] のように最低バージョンを要求できます。パッケージを書くなら対応する命令は \ProvidesPackage{mypackage}[2026/01/01 v1.0 ...] で、\NeedsTeXFormat は両者に共通です。鉄則として、\ProvidesClass の名前は実際のファイル名と一致させます——myclass.cls の中には必ず \ProvidesClass{myclass} と書いてください。

latex
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2026/01/01 v1.0 My example class]

オプションを宣言する — \DeclareOption と \CurrentOption

クラスが受け取れるオプションは \DeclareOption{option}{code} で一つずつ宣言します。利用者がそのオプションを指定すると、後述の \ProcessOptions に到達した時点で code が実行されます。中身は任意の LaTeX の構成で構いませんが、実際にいちばん多いのは \newif で作った真偽フラグを立てるだけの一行です——重い処理はフラグを見て後から行うほうが、順序の事故が起きません。

宣言していないオプションの受け皿が、星付きの \DeclareOption*{code} です。この中では \CurrentOption が「いま処理しようとしているオプション名」に展開されます。自作クラスでいちばんよく書かれる一行が、これを使って未知のオプションを土台クラスへそのまま転送するものです。この一行があるおかげで、10pta4paper を自分で宣言しなくても、利用者は当たり前のように渡せます——冒頭で見た「クラスは黙って渡す」という既定を、意図した宛先に向け直しているわけです。

latex
% pass anything we do not handle ourselves on to article
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

オプションを処理し、土台クラスを読み込む — \ProcessOptions と \LoadClass

宣言だけでは何も起きません。\ProcessOptions を呼んで初めて、選ばれたオプションのコードが実行されます。実務ではほぼ常に \ProcessOptions\relax と書きます。星付きの \ProcessOptions* という別形があるため、末尾の \relax で「星なしのほう」を確実に選び、無駄な先読みと紛らわしいエラーを防ぐ——clsguide も明示的にそう勧めています。星なしは 宣言した順 に、星付きは 呼び出し側が並べた順 にオプションを処理します。

ゼロから版面を組むのは割に合わないので、ほとんどの自作クラスは既存クラスを土台にします。それが \LoadClass[options]{article} で、article.cls の命令と体裁を丸ごと読み込みます。この命令は クラスファイルの中でしか使えず、一つのクラスファイル内で一度きり です。そして順序が重要——利用者が \documentclass[...] で渡したオプションを土台クラスへ効かせるため、\LoadClass はオプション処理(\ProcessOptions)の後に置きます。転送設定を書く → \ProcessOptions で振り分ける → 土台を読み込む、という並びです。受け取ったオプションをそっくり渡したいだけなら \LoadClassWithOptions{article} が便利で、パッケージを書くときは \LoadClass の代わりに \RequirePackage、そっくり渡すなら \RequirePackageWithOptions を使います。

\LoadClass の後ろが、いよいよ このクラスらしさ を書く場所です。\renewcommand で見出しの体裁を変え、\setlength で余白を調整し、\newcommand / \newenvironment で新しい命令や環境を定義する。必要な追加パッケージも、ここから \RequirePackage で読み込みます。逆に言えば、\LoadClass より前に置いてよいのはオプションの宣言と処理だけだと覚えておくと、順序で悩まなくなります。

完成例:article を拡張する最小クラス

以上を一つにまとめると、次の最小の .cls になります。article を土台に、独自の draft オプションを足し、未知のオプションは article へ転送し、既定として a4paper を流し込み、最後に余白と節番号の体裁を自分好みにしています。これを myclass.cls として原稿と同じフォルダに置き、文書側で \documentclass[11pt,a4paper,draft]{myclass} と書けば使えます。

latex
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2026/01/01 v1.0 My example class]

% --- declare options ---
\newif\if@my@draft \@my@draftfalse
\DeclareOption{draft}{\@my@drafttrue}
% forward everything else to article
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

% --- defaults, then execute, then load the base class ---
\ExecuteOptions{a4paper}
\ProcessOptions\relax
\LoadClass{article}

% --- this class's own character ---
\RequirePackage[margin=25mm]{geometry}
\setlength{\parindent}{0pt}
\renewcommand{\thesection}{\Alph{section}}
\if@my@draft
  \AtBeginDocument{\typeout{myclass: DRAFT MODE}}
\fi

\endinput

末尾の \endinput は「このファイルはここまで」と LaTeX に伝える区切りで、慣習として付けます。以降に書いたメモやサンプルは読み込まれません。パッケージにしたいときは、\ProvidesClass\ProvidesPackage に、\LoadClass\RequirePackage に置き換えれば、同じ骨格がそのまま .sty になります。

新しい流儀 — \DeclareKeys と \ProcessKeyOptions

\DeclareOption は今も完全に有効ですが、扱えるのは「あるかないか」のスイッチが中心で、logo=acme.pdf のような 値を持つオプション は自力で解析する羽目になります。そこで LaTeX カーネル自身がキー・バリュー方式を用意しました。\DeclareKeys でキーを宣言し、\ProcessKeyOptions で処理します。宣言のときキー名に付ける「プロパティ」が挙動を決め、基本のものは .code(任意のコードを実行)、.if / .ifnot(TeX の真偽スイッチを立てる)、.store(値をマクロに保存)、.usage(読み込み時のみ/プリアンブル内/制限なし、を指定)です。未知のキーは \DeclareUnknownKeyHandler で受け、\ProcessKeyOptions を呼べば \ProcessOptions を別に呼ぶ必要はありません

latex
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{keyclass}[2026/01/01 v1.0 Key-value demo class]

\DeclareKeys[keyclass]{
  draft.if   = @keyclass@draft,
  logo.store = \@keyclass@logo,
  logo.usage = load
}
% anything that is not one of our keys goes to article
\DeclareUnknownKeyHandler[keyclass]{%
  \PassOptionsToClass{\CurrentOption}{article}}
\ProcessKeyOptions[keyclass]   % no \ProcessOptions needed
\LoadClass{article}

\endinput

この仕組みはもともと l3keys2e パッケージが提供していたもので、その中核が LaTeX2ε カーネルに取り込まれました(TeX Live 2024 に同梱されるカーネルは LaTeX2e 2023-11-01 で、\DeclareKeys\ProcessKeyOptions\SetKeys を備えています)。ただし既存のパッケージには l3keys2e を読み込む書き方も残っていて、たとえば jlreq.cls は冒頭で \RequirePackage{l3keys2e} を実行しています。使い分けは単純です——値を取るオプションが一つでもあるなら \DeclareKeys、オンオフのスイッチだけなら \DeclareOption で十分。読み込み後に設定を変えたい場合は \SetKeys を使います。

配布前に走らせる最小のテスト

クラスは読み込まれた瞬間に文書全体へ影響するので、本文を書き始める前に小さなテスト文書で挙動を固めます。確認するのは二点だけ——11pttwocolumn のような標準オプションが土台クラスへ届いているか、そして自分の独自オプションだけが自作コードで処理されているか。ここで期待どおりに動かないなら、原因はほぼ確実に \ProcessOptions の位置、\DeclareOption* の転送、\LoadClass の順序のいずれかです。

latex
\listfiles                     % log every file and version that is loaded
\documentclass[11pt,a4paper,draft]{myclass}
\begin{document}
\section{Smoke test}
Check the body size, the paper, the draft switch,
the heading style and the margins.
\end{document}
  • ログに名乗りが出るか。 Document Class: myclass ... の行と、\ProvidesClass に書いた日付・バージョンが .log に出ることを確かめます。ファイル名とクラス名が食い違うと、後々必ず混乱します。
  • 標準オプションを壊していないか。 11pttwocolumn が無視されるなら、\DeclareOption* の転送か \LoadClass の位置を見直します。
  • わざと綴りを間違えてみる。 \documentclass[nosuchoption]{myclass} を通し、ログに Unused global option(s) が出ることを確認します。出なければ、転送先のパッケージが黙って呑み込んでいます。
  • \endinput 以降を空にする。 メモやサンプルを末尾に残すと、区切りが失われたときに予期しない文字列が読み込まれます。

配布する形まで考える

自作クラスの真価は、自分の手元で動いた瞬間ではなく、他人が別の環境で読み込んだ瞬間 に問われます。最低限、.cls、短いサンプル文書、README、変更履歴を同じディレクトリに置き、サンプルがそのまま通ることを確認してください。README では「土台クラスへ転送するオプション」と「自作クラスだけが受け取るオプション」を分けて書きます。そうすれば利用者は 11pt がどこで効いているのかを追えます。規模が大きくなったら、LaTeX 標準の docdocstrip を使い、.dtx にソースと解説をまとめて .ins から .cls を生成する形にすると、配布と文書化が一本化されます。

terminal
myclass/
  myclass.cls
  sample.tex
  README.md
  CHANGELOG.md

最後に、サンプルには \listfiles を入れておきます。ログの末尾に、読み込まれたファイルとそのバージョンが一覧で出るので、利用者が古い myclass.cls をローカルに抱えていないか、必要なパッケージが想定どおり読まれているかを、一目で切り分けられます。クラスは文書全体の土台です。本文用のマクロを一つ足すより、読み込み順・オプション処理・ログの情報を丁寧に整えるほうが、長く使われる文書では確実に効きます。