読者です 読者をやめる 読者になる 読者になる

ボレロ村上 - ENiyGmaA Code

中3女子です。

コンパイル時ズンドコキヨシ

中3女子です。


巷でズンドコキヨシなるものが流行っていたようなので C++11 constexpr でやってみた。


コード:

#include <cstddef>
#include <type_traits>
#include <stdexcept>
#include <sprout/utility/any_convertible.hpp>
#include <sprout/type_traits/enabler_if.hpp>
#include <sprout/array.hpp>
#include <sprout/string.hpp>
#include <sprout/random/default_random_engine.hpp>
#include <sprout/random/bernoulli_distribution.hpp>
#include <sprout/random/unique_seed.hpp>
#include <sprout/generator/functions.hpp>
#include <sprout/range/adaptor/reversed.hpp>
#include <sprout/algorithm/string/join.hpp>

static constexpr std::size_t default_limit = 96;

static constexpr std::size_t len = std::extent<std::remove_reference<decltype("キ・ヨ・シ!")>::type>::value - 1;
typedef sprout::string<len> string_type;

template<std::size_t N>
struct zundoko_result
    : public sprout::algorithm::results::join<sprout::array<string_type, N> >
{};
template<std::size_t N>
using zundoko_result_t = typename zundoko_result<N>::type;

inline constexpr string_type
zundoko(bool c) {
    return !c
        ? sprout::to_string("ズン")
        : sprout::to_string("ドコ")
        ;
}

template<
    std::size_t N, typename Rnd, typename... Args,
    typename sprout::enabler_if<!(sizeof...(Args) + 6 <= N)>::type = sprout::enabler
>
inline constexpr typename zundoko_result<N>::type
kiyoshi(Rnd const&, bool, bool, bool, bool, Args...) {
    return throw std::runtime_error("exceeded"), sprout::any_convertible();
}
template<
    std::size_t N, typename Rnd, typename... Args,
    typename sprout::enabler_if<(sizeof...(Args) + 6 <= N)>::type = sprout::enabler
>
inline constexpr typename zundoko_result<N>::type
kiyoshi(Rnd const& rnd, bool e0, bool e1, bool e2, bool e3, Args... args) {
    return !sprout::generated_value(rnd) || e0 || e1 || e2 || e3
        ? kiyoshi<N>(sprout::next_generator(rnd)(), sprout::generated_value(rnd), e0, e1, e2, e3, args...)
        : sprout::algorithm::join(
            sprout::array<string_type, N>{{
                sprout::to_string("キ・ヨ・シ!"),
                zundoko(sprout::generated_value(rnd)),
                zundoko(e0), zundoko(e1), zundoko(e2), zundoko(e3),
                zundoko(args)...
                }}
                | sprout::adaptors::reversed
            )
        ;
}
template<
    std::size_t N, typename Rnd, typename... Args,
    typename sprout::enabler_if<(sizeof...(Args) + 1 <= 4)>::type = sprout::enabler
>
inline constexpr typename zundoko_result<N>::type
kiyoshi(Rnd const& rnd, Args... args) {
    return kiyoshi<N>(sprout::next_generator(rnd)(), sprout::generated_value(rnd), args...);
}

template<
    std::size_t N = default_limit, typename URNG,
    typename sprout::enabler_if<!std::is_integral<URNG>::value>::type = sprout::enabler
>
inline constexpr typename zundoko_result<N>::type
zundoko_kiyoshi(URNG const& urng) {
    return kiyoshi<N>(sprout::bernoulli_distribution<>()(urng));
}
template<std::size_t N = default_limit>
inline constexpr typename zundoko_result<N>::type
zundoko_kiyoshi(std::size_t seed) {
    return zundoko_kiyoshi<N>(sprout::default_random_engine(seed));
}
template<std::size_t N = default_limit>
inline constexpr typename zundoko_result<N>::type
zundoko_kiyoshi() {
    return zundoko_kiyoshi<N>(SPROUT_UNIQUE_SEED);
}

#include <iostream>

int main() {
    constexpr auto result = zundoko_kiyoshi();
    std::cout << result;
}

出力:

ズンドコズンズンドコズンズンドコドコズンズンドコドコズンドコズンズンドコドコズンドコドコズンドコドコドコドコズンドコズンドコズンズンズンズンズンドコキ・ヨ・シ!

犬小屋でコンパイル&実行:
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

処理の流れ

  1. コンパイル時乱数生成エンジンを用意する
  2. ベルヌーイ分布で false/true(ズン/ドコ)の乱数を発生させ、可変引数テンプレートのパラメータパックに追加していく
    • パラメータパックは逆順になる(生成順が {ズン, ズン, ズン, ズン, ドコ} なら Args = {ドコ, ズン, ズン, ズン, ズン} になる)
    • → なぜなら、パラメータパックは末尾より先頭から取り出すほうが効率的だから
  3. {ズン, ズン, ズン, ズン, ドコ} 列になったら各要素を文字列に変換して "キ・ヨ・シ!" を追加した配列を作る
  4. reversed アダプタで配列を反転する
  5. join で結合した文字列を返す
  6. (実行時)文字列を出力する


これら処理を書くために Sprout ライブラリのうち以下のコンポーネントを使った。

  • constexpr Arrayクラス
  • constexpr 文字列クラス
  • constexpr 乱数
  • constexpr Rangeアダプタ
  • constexpr 文字列アルゴリズム


これは C++11 で動くように書いたが、C++14 ならば当然もっと簡単に書けるだろう。

InDesign でラノベの組版をする

中3女子です。

今度友人に InDesign組版を教える機会ができたので、資料作成ついでにブログ記事に書くことにしました。同人ラノベ組版InDesign で行ってゆくチュートリアルです。

版面の設計

まず版面(はんづら)を決めましょう。版面とは本文のレイアウトのことです。
即売会で小説同人誌を買うとさまざまな本文デザインの同人誌があります。独創的なデザインで作品の世界観を引き立てているものもあれば、中には読みづらくて読書意欲を損ねるようなものも少なくありません。
読書意欲を損ねるような版面とは、たとえば
・フォントがやたらでかい、もしくは細かすぎ
・行間をとりすぎてスカスカ、もしくは詰まりすぎ
などがあります。
単純なことですが、これが上手くできているかどうかで文章のクォリティまで違って見えてきます。一番無難なのは、手元にある商業レーベルのラノベを実際に定規で測って参考にすることです。自分がいま標準にしている文庫判の版面は次のようなものです。おおよそ電撃文庫を元にしています。

  • 本文:11.5Q/A-OTF リュウミン Pr5 L-KL
  • 行間:7H
  • 行文字数:42/行数:17
  • 地:12mm/ノド:18mm (天:15.25mm/小口:10.125mm)

f:id:boleros:20160301191653j:plain


本文のフォントはリュウミンL-KLです。これは電撃文庫でいま使われているものと同じものです。ちなみに暗黒定数式では本文にI-OTF明朝オールドPro Rを使っています。これも商業レーベルでリュウミンと同じくらい広く使われているフォントです。これらはいずれも有料の商用フォントです。このほか、同人小説の本文ではヒラギノ明朝や小塚明朝もよく使われているようです。
本文のフォントサイズは 11.5Q。"Q"とは日本の印刷業界でよく使われる「級」単位で、1Q=0.25mm です。つまり 11.5Q は 2.875mm/文字、ポイント換算すると約8.2ポイントになります。


行間は 7H。"H"は"Q"と同じ単位なのですが、アキを表現するときは"H"と表記されます。7H は本文 11.5Q に対して約60%になります。ルビのサイズは一般的に本文の50%になるため行間は少なくとも50%必要で、50~70%くらいが読みやすいとされています。
自分もそうでしたが初心者は文字が潰れるのをおそれてフォントや行間をつい大きめにとってしまいがちですが、商業レーベルのそれは素人の感覚よりも実際はかなりコンパクトになっています。書いてしまうと単純なことですが、ここを気をつけると俄然プロっぽく読みやすい版面になります。


行文字数42、行数17は電撃文庫ガガガ文庫など大手ラノベレーベルで用いられている数値です。
地/ノドは知らないと分かりにくい用語ですが、版面をページに配置するときどの方向を基準にするかという意味です。天・地はそれぞれ本の上下、小口・ノドはそれぞれ本の外側と内側(綴じる側)を指します。つまり「地:12mm/ノド:18mm」は下側から12mm、内側から18mmそれぞれアキができるように版面を配置するということです(この場合、天/小口は自動的に決まります)。
ちなみにノド18mmというのは、多くの商業レーベルのラノベよりもかなり広めにとってあります。これには理由があって、同人印刷の平綴じは一般に製本の糊付けが固めでノド側が開きにくい傾向にあり、ノド側のアキを十分に取っていないと文字が隠れたり見づらくなる場合があるためです。最低でも 15mm をとっておくと安心と言えるでしょう。

ファイルを作成する

最初に決めた版面に沿った新規ファイルを作成します。
メニューの [ファイル]→[新規]→[ドキュメント] を選択、もしくは [Ctrl]+N で新規ドキュメントのダイアログを出します。


f:id:boleros:20160301191811j:plain
ページサイズには一般的な文庫判であるA6の寸法 105mm×148mm を入力し、「マスターにテキストフレーム」をチェックします。「裁ち落とし」は 3mm そのままにしておきます。
裁ち落としとは、印刷・製本後の裁断のときに切り落とされるフチの部分のことです。このフチの部分を余分にとらず仕上がりサイズギリギリで作ってしまうと、裁断のわずかなズレでページの周囲に白い余白が出てしまうことになります。小説本文の場合フチはもともと余白のことが多いですが、ページ一杯のイラストやベタがあると問題になるので、裁ち落としは必ず設定するようにしましょう。この裁ち落とし 3mm という数値は同人印刷に限らず、チラシやポスターなどほとんどの印刷通販で共通に使われています。
入力が済んだら [レイアウトグリッド] ボタンを選択してください。


f:id:boleros:20160301191923j:plain
新規レイアウトグリッドのダイアログでは、版面設計で決めた数値を入力していきます。この設定を後から作業が進んでから変えようとするといろいろ面倒なので、版面は最初にきっちり設計して後から弄らなくても済むようにしましょう。
[OK] を選択すると作業画面に移ります。

文字スタイル設定

最初にすべき作業は文字スタイルを作成することです。これをせずにどんどん作業を進めると後でえらく苦労することになります。
文字スタイルとは、同じ種類(たとえば本文、見出し、柱、など)の文字をカテゴリ分けしておくことで、一括で設定・変更ができる機能です。
なぜこれが重要かというと、小説本文にはふつうの文章だけでなく見出しやエピグラフ、あるいは一部分だけ簡体字に変えたりなど、異なるフォント設定が混在することがあるからです。もし本文の一部分だけを簡体字フォント SimSun に設定して、後からやはり Adobe Song に変更しようと思った場合、SimSun で手動検索して一つずつ置換していかねばなりません。手動置換は手間もさることながらミスの原因にもなります。このような場合、あらかじめ「簡体字」文字スタイルを作成し適用しておくことで、文字スタイルの設定を弄れば適応された部分すべてに対して一度に変更を反映できます。


f:id:boleros:20160301192124j:plain
文字スタイルを作成するには、[文字スタイル] パネル下部の [新規スタイルを作成] を選択します。追加されたスタイルをダブルクリックして設定を入力してゆきます。


f:id:boleros:20160301192139j:plain
まず基本となる「本文」スタイルを作成しましょう。
ここでは「本文」という名前だけを入力します。入力しない空欄は、ファイル作成時のレイアウトグリッド設定がそのまま使われるので問題ありません。


f:id:boleros:20160301192157j:plain
f:id:boleros:20160301192213j:plain
次に「見出し」スタイルを作成します。
[基準] を先ほど作成した「本文」に設定します。スタイルに基準を設定すると、その基準となるスタイルを下敷きとして引き継ぎます。つまり「本文」スタイルの設定を変更すると、それを基準とする「見出し」スタイルにも同様の設定が反映されるのです。
見出しは本文よりもすこし大きい 13Q に設定します。


f:id:boleros:20160301192229j:plain
f:id:boleros:20160301192238j:plain
「ノンブル」スタイルを作成します。ノンブルとはページ毎に振られるページ番号のことです。ノンブルは読者のためにだけあるのではなく、印刷製本業者が丁合を確認するのに必須なのですべてのページに記す必要があります。
Adobe Garamond Pro Regular、10Q、オールドスタイル数字 を設定します。
このノンブルのフォント Garamond については、商業レーベルに依らず自分の趣味で選びました。理由はオールドスタイル数字が使えるためです。オールドスタイル数字とは、数字の上下が揃わずに(アルファベットの"h"やq"のように)ラインが上下に出る書き方のことです。ようするにちょっとオシャレな数字です。商業レーベルでいうと星海社FICTIONSのノンブルなどがオールドスタイル数字になっています。


f:id:boleros:20160301192354j:plain
「柱」スタイルを作成します。柱とはページ毎の余白に置かれるタイトルもしくは章題のことです。
本文よりすこし小さな 8Q を設定します。


f:id:boleros:20160301192442j:plain
f:id:boleros:20160301192454j:plain
例に出した「簡体字」スタイルを作成します。
[基準] を「本文」に設定します。フォントを Adobe Song Std L に設定します。


このように、あらかじめ必要な文字スタイルを作成しておき、これから配置する文章に適応してゆけば、文字の設定や変更を一度に反映させることだできるようになります。s

マスターページ設定

次にすることはマスターページ設定です。
マスターページとは、同じレイアウトのページの設定をまとめて管理するための雛形です。マスターページにノンブルや柱を配置しておくことで、同じマスターページを設定されたページにも自動的にノンブルや柱が配置されます。マスターページは本文、扉、イラストなど必要なレイアウトに応じて自由に作成することができます。


f:id:boleros:20160301192531j:plain
[ページ] パネル上部の欄がマスターページ一覧です。余白を右クリックし、[新規マスター] を選択しましょう。


f:id:boleros:20160301192541j:plain
新規マスターのダイアログで、名前と基準マスターを入力します。
ここでは「本文」マスターを作成します。プレフィックスは何でもよいですが、複数InDesign ファイルを結合する場合に重複避けになるので、適当な固有キャラクタを入れてください。
基準マスターとは、このマスターページの親となるマスターです。基準マスターを設定すると、そのマスターページを下敷きとしてそれに書き加えるような形になります。ここでは一番最初から存在する「A-マスター」を基準に設定します。


作成した「本文」マスターの左右にノンブル(と柱)のテキストボックスを配置します。位置は(天:8mm/小口:11mm)とします。


f:id:boleros:20160301192835j:plain
f:id:boleros:20160301192907j:plain
ノンブルを入力するには [書式]→[特殊文字の挿入]→[マーカー]→[現在のページ番号] を選択し、入力された文字に「ノンブル」スタイルを適用します。


f:id:boleros:20160301193042j:plain
同様に、柱としてタイトルを打ち込み、「柱」スタイルを適用します。


これで「本文」マスターの設定ができました。
ここまで設定できたら、一度ファイルを別名で保存してバックアップしておいたほうがよいでしょう。一度ひととおり設定が済んだテンプレートをとっておけば、後で同じ版面な別のテキストを作成するときに使い回せるからです。

文章の配置

いよいよ文書を配置してゆきます。


f:id:boleros:20160301193106j:plain
f:id:boleros:20160301193132j:plain
まずは [ページ] パネル下側の空欄を右クリックし [ページ挿入] を選択。ページは流し込むテキストの長さに応じて自動で拡充されるので、ここでは 1ページだけで構いません。また、ファイル新規作成時に置かれたデフォルトのページは消してしまって結構です。


f:id:boleros:20160301193205j:plain
f:id:boleros:20160301193223j:plain
あらかじめ [文字スタイル] パネルの「本文」スタイルを選択しておきます。[ファイル]→[配置] を選択し、[配置] ダイアログでテキストファイルを開きます。


f:id:boleros:20160301193236j:plain
カーソルアイコンが変化したらテキストフレームの一番右上マスをクリックすると、テキストが流し込まれます。


f:id:boleros:20160301193319j:plain
デフォルトの設定では鉤括弧などの約物が半角で表示されてしまいます。テキストを全選択し、[段落] パネルの [文字組み] を [約物全角] に設定することで、全角で表示されるようになります。


f:id:boleros:20160301193335j:plain
あとは見出し等のテキストを選択して文字スタイルを適応してゆきます。


f:id:boleros:20160301193402j:plain
縦中横を適用するには対象テキストを選択し、上部パネルの [縦中横] をチェックします。縦中横とは、半角英数字を縦書きにする機能です。


f:id:boleros:20160301193456j:plain
f:id:boleros:20160301193502j:plain
ルビを振るには対象テキストを選択し、[Alt]+[Ctrl]+R で [ルビ] ダイアログを開き、ルビを入力します。[種類] のうち [モノルビ] は一文字に対してルビを振る場合、複数文字に対してルビを振る場合は [グループルビ] を選択します。

テキスト編集の注意点

文字列コピペのショットカットキーは罠なので注意。
InDesign は、コピーしたプレーンテキストを本文にそのまま [Ctrl]+V でペーストすると、本文とは異なるスタイルでペーストされてしまいます。「本文」スタイルで記述されているところに同じ「本文」スタイルでテキストをペーストする場合は、必ず [Alt]+[Ctrl]+[Shift]+V でペーストするようにしましょう。めっちゃ押しづらいけど我慢。

一旦ここまで

以上で、とりあえず基本的な文字組みはだいたいできるでしょう。
あと必要な知識としては、レイヤー、合成フォント、隠しノンブル、画像の配置、ページの移動、書き出し設定などがありますが、それはまたの機会に。
InDesignAdobe製品の中でもわりと挙動に癖がある方なので、操作感はとりあえず触ってみて覚えましょう。
作家の京極夏彦などはDTPまで自分でやることで有名で、執筆も InDesign 上でやるといった噂までありますが、試しにやってみたらこれが結構面白かったので暇な人は試してみるとよいでしょう。

C++初心者会で話題になったClangの相互再帰バグについて

中3女子です。


歌舞伎座.tech#8「C++初心者会」という勉強会が先日あり、自称初心者やクソザコによるさまざまな発表がおこなわれた。自分は参加できなかったので、いくつかの発表をニコ生で視聴した。


その中に、@wx257osn2 氏による constexpr ラムダライブラリを実装したという発表があった。実装にあたっては Clang のバグに対処するワークアラウンドを書くのに苦労したという。さもありなん。Clang は全体的な規格準拠度ではおおむね GCC 以上といってよいが、constexpr 関係ではいまだに致命的なバグを残している。それがどのようなバグなのか応答で齟齬があったようなので、脇からの補足をここに記しておく。


Clang の constexpr 関係の致命的なバグとは、相互再帰におけるバグである。相互再帰する constexpr 関数テンプレートを実体化すると、テンプレートインスタンス化が無限再帰してコンパイルエラーになる。具体的には以下のようなコードがエラーとなる。

template<typename T>
constexpr T f(T const&);
template<typename T>
constexpr T g(T const&);
 
template<typename T>
constexpr T f(T const& val) { return g(val); }
template<typename T>
constexpr T g(T const& val) { return val >= 10 ? val : f(val + 1); }
 
int main() {
  constexpr auto x = f(0);
  // [clang version 3.2]
  // fatal error: recursive template instantiation exceeded maximum depth of 512
}

犬小屋でのコンパイル結果→ [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ http://melpon.org/wandbox/permlink/KabKOkFRecUo1eD1


f() は g() を呼び出し、g() は f() を呼び出すため相互再帰となる。しかしインスタンス化されるシグネチャはそれぞれ f(int const&) と g(int const&) であり、再帰によって変化しない。そのためこのコードは規格的にもインスタンス化において問題はない。GCC でも問題なくコンパイルが通る。にもかかわらず Clang ではテンプレートインスタンス化の処理が無限に再帰して停止していないためエラーになっている。これは明確に処理系のバグであり、Clang 3.2 から 3.7(trunk) 現在にいたるまで修正されていない。


この相互再帰バグに対してどのようなワークアラウンドを書くか。もっとも単純な手は、テンプレートインスタンス化の深さをカウントして、あらかじめ決めた回数に達したら SFINAE でインスタンス化を打ち切ることだ。例えば上記コードは以下のようにワークアラウンドを書くことができる。

#include <type_traits>
#include <stdexcept>

#define LIMIT 256
extern void* enabler;

template<int D = 16, typename T, typename std::enable_if<(D < LIMIT - 1)>::type*& = enabler>
constexpr T f(T const&);
template<int D = 16, typename T, typename std::enable_if<!(D < LIMIT - 1)>::type*& = enabler>
constexpr T f(T const&) {
  return std::runtime_error("recursive template instantiation exceeded maximum depth"), T();
}
template<int D = 16, typename T, typename std::enable_if<(D < LIMIT - 1)>::type*& = enabler>
constexpr T g(T const&);
template<int D = 16, typename T, typename std::enable_if<!(D < LIMIT - 1)>::type*& = enabler>
constexpr T g(T const&) {
  return std::runtime_error("recursive template instantiation exceeded maximum depth"), T();
}

template<int D, typename T, typename std::enable_if<(D < LIMIT - 1)>::type*&>
constexpr T f(T const& val) { return g<D + 1>(val); }
template<int D, typename T, typename std::enable_if<(D < LIMIT - 1)>::type*&>
constexpr T g(T const& val) { return val >= 10 ? val : f<D + 1>(val + 1); }

int main() {
  constexpr auto x = f(0);
}

犬小屋でのコンパイル結果→ [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ http://melpon.org/wandbox/permlink/kePnBBddZLlZQcxo


テンプレート引数 D は深さのカウンタである。インスタンス化の深さがリミットに達すると、SFINAE によってランタイムエラー例外を投げる版が選択される。この版は f() または g() をそれ以上呼び出さないため、インスタンス化はここで停止する。「実際の」呼び出しが深さ制限を越えないかぎりはコンパイルエラーにならない。このようにして Clang の相互再帰バグを回避できる。


(なお、もし深さ制限を超えて「実際の」呼び出しがおこなわれた場合、それがコンパイル時であれば throw 式(定数式でない)が評価されコンパイルエラーになり、実行時であれば例外が投げられる)


このワークアラウンドの問題点は見てのとおりコードが非常に長く醜くなることだ。関数を分けるだけでなく、それぞれにカウンタと enable_if を埋め込まなければならない。相互再帰が f() → g() → f() のふたつの間だけでなく f() → g() → h() → i() → f() のように多くの中間関数を介するならば、そのすべてに同様のワークアラウンドを仕込む必要がある。例えば次のように。
Github: https://github.com/bolero-MURAKAMI/Sprout/blob/master/sprout/random/uniform_int_distribution.hpp#L134
これは悪夢でしかない。

醜いワークアラウンドを書かない方法

もし可能であれば、相互再帰を避けるような実装がもっとも望ましい。設計上回避できない場合もあるがそうでない場合もある。constexpr 関数の実装で相互再帰が出現するのは、C++11 constexpr の制限下で複雑なループを記述するときに多い。そのようなケースで相互再帰そのものを回避できる実装のアイディアを最近思いついた。


sprout::while_loop がそれだ。
Github: https://github.com/bolero-MURAKAMI/Sprout/blob/master/sprout/utility/while_loop.hpp


while_loop() は引数として(状態の初期値、ループ終了を判定する叙述関数、次の状態を返す単項オペレータ)をとる。名前は while_loop だが挙動においては for 文に近い。処理の内部ではループが二分探索的な分割統治として行われ(このイディオムを倍分再帰と呼ぶ)、相互再帰は現れない。多重ループもオペレータ内からの更なる while_loop 呼び出しとして記述され、これも相互再帰を必要としない。最近実装したものなので使用例は少ないが、以下のように使う。
Github: https://github.com/bolero-MURAKAMI/Sprout/blob/master/sprout/random/linear_congruential.hpp#L148


また、相互再帰を避けるためだけでなく、constexpr でのループの実装のために fun_impl_1(), fun_impl_2(), ... といった実装用関数が乱立するのを防ぐ意味でも効果があると思われる。

コンパイラ毎の constexpr 関係のバグとワークアラウンドについて

以下に書き留めておくことにしている。
Sprout C++ Library Wiki: コンパイラ性能比較 http://www.boleros.x0.com/doc/sproutwiki/index.php?%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E6%80%A7%E8%83%BD%E6%AF%94%E8%BC%83


が、筆無精なので主要ないくつかしか記していないし最新の VC++ 等の検証は進んでいない。そのうち充実したいがいつになるかわからない。constexpr に明るい有志には加筆を願いたい。

コンパイル時Brainfuckコンパイラ ――C++14 constexpr の進歩と限界――

中3女子です。
この記事は C++ Advent Claneder2014 23日の参加記事です。

constexpr とは

この記事を読んでいる層には、いまさら言及するまでもないと思われるので省略する。
必要であれば このあたり の資料を参照のこと。

C++14 シンタックス上の進歩とセマンティクス上の進歩

巷では C++14 になり constexpr が簡単になったという話をしばしば耳にする。たしかに簡単になった。
では何が簡単になったかというと、もっともわかりやすいのは複文とローカル変数、制御構文の制限緩和だろう。
条件分岐やループを if, while, for, switch 等でガリガリ書くことができるようになった(ただし goto は不可だ)。


非常に簡単になったといえるが、これはあくまでシンタックス上の進歩だ。
これによって何ができるかというセマンティクス上の問題については、C++11 の時とほとんど変わっていない。
これら制御構文によるコードは、条件演算子再帰、Index-tupleイディオム、分割統治といったイディオムによって、見た目はともかく挙動と効率においてほとんど変わらずに C++11 constexpr の制約を満たすように書きかえることができる。
constexpr の進歩を語るには、単に簡単になったと言うだけでなく、何ができるようになったかという論点でも語るべきである。


さて、C++14 constexpr で大きなセマンティクス上の進歩をもたらしたのは、オブジェクト書き換えの制限緩和だ。
C++14 では、constexpr関数内で、ローカル変数や一時オブジェクトの内容を書き換えることができるようになった(ただし寿命が定数式評価の中で完結するものに限る)。
これによって、テンポラリバッファを計算途中の値で書き換えながら実行するようなアルゴリズムが記述できるようになった。
C++11 では不可能であるか、もしくは非常に非効率にしか記述できなかったことである。

DFT と FFT

DFT(離散フーリエ変換)をコンパイル時に行いたいという需要は多い[要出典]
Sprout にもコンパイル時DFTの機能がある。

/*=============================================================================
  Copyright (c) 2011-2014 Bolero MURAKAMI
  https://github.com/bolero-MURAKAMI/Sprout

  Distributed under the Boost Software License, Version 1.0. (See accompanying
  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#ifndef SPROUT_NUMERIC_DFT_FIXED_DFT_HPP
#define SPROUT_NUMERIC_DFT_FIXED_DFT_HPP

#include <iterator>
#include <type_traits>
#include <sprout/config.hpp>
#include <sprout/index_tuple/metafunction.hpp>
#include <sprout/container/traits.hpp>
#include <sprout/container/functions.hpp>
#include <sprout/container/indexes.hpp>
#include <sprout/iterator/operation.hpp>
#include <sprout/iterator/dft_iterator.hpp>
#include <sprout/algorithm/fixed/results.hpp>
#include <sprout/pit/pit.hpp>
#include <sprout/math/less.hpp>
#include <sprout/numeric/dft/dft_element.hpp>
#include <sprout/detail/container_complate.hpp>

namespace sprout {
    namespace fixed {
        namespace detail {
            template<typename RandomAccessIterator, typename Result, sprout::index_t... Indexes>
            inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type
            dft_impl_ra(
                RandomAccessIterator first, RandomAccessIterator last, Result const& result,
                sprout::index_tuple<Indexes...>,
                typename sprout::container_traits<Result>::difference_type offset,
                typename sprout::container_traits<Result>::size_type size,
                typename sprout::container_traits<Result>::size_type input_size
                )
            {
                return sprout::remake<Result>(
                    result, size,
                    (Indexes >= offset && sprout::math::less(Indexes, offset + size) && sprout::math::less(Indexes, offset + input_size)
                        ? sprout::detail::dft_element_impl(first, last, Indexes - offset, input_size)
                        : *sprout::next(sprout::internal_begin(result), Indexes)
                        )...
                    );
            }
            template<typename RandomAccessIterator, typename Result>
            inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type
            dft(
                RandomAccessIterator first, RandomAccessIterator last, Result const& result,
                std::random_access_iterator_tag*
                )
            {
                return sprout::fixed::detail::dft_impl_ra(
                    first, last, result,
                    sprout::container_indexes<Result>::make(),
                    sprout::internal_begin_offset(result),
                    sprout::size(result),
                    sprout::distance(first, last)
                    );
            }

            template<typename ForwardIterator, typename Result, typename... Args>
            inline SPROUT_CONSTEXPR typename std::enable_if<
                sprout::container_traits<Result>::static_size == sizeof...(Args),
                typename sprout::fixed::results::algorithm<Result>::type
            >::type
            dft_impl(
                ForwardIterator, ForwardIterator, Result const& result,
                typename sprout::container_traits<Result>::size_type,
                ForwardIterator, typename sprout::container_traits<Result>::difference_type,
                Args const&... args
                )
            {
                return sprout::remake<Result>(result, sprout::size(result), args...);
            }
            template<typename ForwardIterator, typename Result, typename... Args>
            inline SPROUT_CONSTEXPR typename std::enable_if<
                sprout::container_traits<Result>::static_size != sizeof...(Args),
                typename sprout::fixed::results::algorithm<Result>::type
            >::type
            dft_impl(
                ForwardIterator first, ForwardIterator last, Result const& result,
                typename sprout::container_traits<Result>::size_type size,
                ForwardIterator first_, typename sprout::container_traits<Result>::difference_type i,
                Args const&... args
                )
            {
                return first != last && sizeof...(Args) < size
                    ? sprout::fixed::detail::dft_impl(
                        sprout::next(first), last, result, size, first_, i + 1,
                        args..., sprout::detail::dft_element_impl(first_, last, i, size)
                        )
                    : sprout::detail::container_complate(result, args...)
                    ;
            }
            template<typename ForwardIterator, typename Result>
            inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type
            dft(
                ForwardIterator first, ForwardIterator last, Result const& result,
                std::forward_iterator_tag*
                )
            {
                return sprout::fixed::detail::dft_impl(first, last, result, sprout::size(result), first, 0);
            }

            template<typename ForwardIterator, typename Result>
            inline SPROUT_CONSTEXPR typename std::enable_if<
                sprout::is_fixed_container<Result>::value,
                typename sprout::fixed::results::algorithm<Result>::type
            >::type
            dft(ForwardIterator first, ForwardIterator last, Result const& result) {
                typedef typename std::iterator_traits<ForwardIterator>::iterator_category* category;
                return sprout::fixed::detail::dft(first, last, result, category());
            }

            template<typename ForwardIterator, typename Result>
            inline SPROUT_CONSTEXPR typename std::enable_if<
                !sprout::is_fixed_container<Result>::value,
                typename sprout::fixed::results::algorithm<Result>::type
            >::type
            dft(ForwardIterator first, ForwardIterator last, Result const& result) {
                return sprout::remake<Result>(
                    result, sprout::size(result),
                    sprout::make_dft_iterator(first, last),
                    sprout::make_dft_iterator(first, last, sprout::distance(first, last))
                    );
            }
        }   // namespace detail
        //
        // dft
        //
        template<typename ForwardIterator, typename Result>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type
        dft(ForwardIterator first, ForwardIterator last, Result const& result) {
            return sprout::fixed::detail::dft(first, last, result);
        }

        template<typename Result, typename ForwardIterator>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type
        dft(ForwardIterator first, ForwardIterator last) {
            return sprout::fixed::dft(first, last, sprout::pit<Result>());
        }
    }   // namespace fixed

    using sprout::fixed::dft;
}   // namespace sprout

#endif  // #ifndef SPROUT_NUMERIC_DFT_FIXED_DFT_HPP


ディスパッチ処理等が含まれているのでコードがいささか煩雑だが、ようするに離散フーリエ変換の定義
{ \displaystyle F(t)= \sum_{x=0}^{N-1} f(x) e^{-i\frac{2 \pi t x}{N}} \quad \quad }
における各 t に対する F(t) を逐次求めている。
時間計算量は標本点 N に対して Ο(N^2) である。
このほか、Sprout.Numeric.DFT にはスペクトル解析といった機能も用意されている。


実際に使ってみると以下のような感じになる。
値の計算はすべてコンパイル時に行われ、実行時には結果のファイルへの出力のみが行われる。

#include <iostream>
#include <fstream>
#include <sprout/config.hpp>
#include <sprout/array.hpp>
#include <sprout/pit.hpp>
#include <sprout/complex.hpp>
#include <sprout/range/numeric/dft.hpp>
#include <sprout/range/adaptor.hpp>
#include <sprout/range/algorithm.hpp>
#include <sprout/functional.hpp>

template<typename Elem, typename Traits, typename InputRange>
std::basic_ostream<Elem, Traits>& output_plot(std::basic_ostream<Elem, Traits>& os, InputRange const& range) {
    os << std::fixed;
    int x = 0;
    for (auto const& e : range) {
        os << x++ << ',' << e << '\n';
    }
    return os;
}
template<typename Elem, typename Traits, typename InputRange>
std::basic_ostream<Elem, Traits>& output_plot_real(std::basic_ostream<Elem, Traits>& os, InputRange const& range) {
    os << std::fixed;
    int x = 0;
    for (auto const& e : range) {
        os << x++ << ',' << real(e) << '\n';
    }
    return os;
}

int main() {
    using namespace sprout;
    namespace adp = sprout::adaptors;

    constexpr std::size_t size = 256;
    typedef array<complex<double>, size> container_t;
    typedef array<double, size> spectrum_t;

    // 周波数 10, 25, 35 の正弦波の重ね合わせを生成
    SPROUT_CONSTEXPR container_t src_data
        = adp::sinusoidal(10. / size, 10000.)
        | adp::transformed(adp::sinusoidal(25. / size, 5000.), plus<double>())
        | adp::transformed(adp::sinusoidal(35. / size, 7500.), plus<double>())
        | adp::copied
        ;
    {
        std::ofstream os("src.txt");
        output_plot_real(os, src_data);
    }

    // DFT(離散フーリエ変換)
    SPROUT_CONSTEXPR auto dft_data = range::dft(src_data, pit<container_t>());

    // IDFT(逆離散フーリエ変換)
    SPROUT_CONSTEXPR auto idft_data = range::idft(dft_data, pit<container_t>());
    {
        std::ofstream os("dft_idft.txt");
        output_plot_real(os, idft_data);
    }

    // 周波数スペクトルに変換
    SPROUT_CONSTEXPR auto amp_spec_data = range::amplitude_spectrum(dft_data, pit<spectrum_t>());
    {
        std::ofstream os("amp_spec.txt");
        output_plot(os, amp_spec_data);
    }

    // 位相スペクトルに変換
    SPROUT_CONSTEXPR auto pha_spec_data = range::phase_spectrum(dft_data, pit<spectrum_t>());
    {
        std::ofstream os("pha_spec.txt");
        output_plot(os, pha_spec_data);
    }
}

なお、このプログラムを完全にコンパイルできるのは現状で gcc 5.0.0(experimental) しかないことに注意されたし。
gcc[trunk] のない読者の環境で結果だけ確認したい場合は、コード中の SPROUT_CONSTEXPR をコメントアウトすればよい。


出力を gnuplot でグラフ化すると以下のようになる。

  • ソース

f:id:boleros:20141224040800p:plain

  • 変換して再度戻す(DFT → IDFT)

f:id:boleros:20141224040806p:plain

f:id:boleros:20141224040811p:plain

  • 位相スペクトル

f:id:boleros:20141224040816p:plain


さて、離散フーリエ変換の効率的なアルゴリズムとして非常に有名な FFT(高速フーリエ変換)があるが、C++11 constexpr の制約下では不可能である。
なぜなら、FFTアルゴリズムは空間の再利用を行うため、オブジェクトの書き換えなしには実装できないからだ。
(同等の挙動を記述することは不可能ではないが、その場合時間計算量は Ο(N^2 log N)よりも悪くなるため意味がない)


C++14 では、constexpr FFT アルゴリズムも実装できる。
もちろん時間計算量も Ο(N log N) になる。

/*=============================================================================
  Copyright (c) 2011-2014 Bolero MURAKAMI
  https://github.com/bolero-MURAKAMI/Sprout

  Distributed under the Boost Software License, Version 1.0. (See accompanying
  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#ifndef SPROUT_NUMERIC_FFT_FFT_HPP
#define SPROUT_NUMERIC_FFT_FFT_HPP

#include <iterator>
#include <sprout/config.hpp>
#include <sprout/complex.hpp>
#include <sprout/math/cos.hpp>
#include <sprout/math/sin.hpp>
#include <sprout/math/constants.hpp>
#include <sprout/utility/swap.hpp>

namespace sprout {
    //
    // fft
    //
    template<typename RandomAccessIterator>
    inline SPROUT_CXX14_CONSTEXPR void
    fft(RandomAccessIterator first, RandomAccessIterator last) {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type value_type;
        typedef typename value_type::value_type elem_type;
        using sprout::real;
        using sprout::imag;
        difference_type const size = last - first;
        // scrambler
        for (difference_type i = 0, j = 1; j < size - 1; ++j) {
            for (difference_type k = size >> 1; k > (i ^= k); k >>= 1)
                ;
            if (j < i) {
                sprout::swap(first[i], first[j]);
            }
        }
        // L shaped butterflies
        elem_type const theta = -(sprout::math::half_pi<elem_type>() / size);
        for (difference_type m = 4; m <= size; m <<= 1) {
            difference_type mq = m >> 2;
            // W == 1
            for (difference_type k = mq; k >= 1; k >>= 2) {
                for (difference_type j = mq - k; j < mq - (k >> 1); ++j) {
                    difference_type j1 = j + mq;
                    difference_type j2 = j1 + mq;
                    difference_type j3 = j2 + mq;
                    value_type x1 = first[j] - first[j1];
                    first[j] += first[j1];
                    value_type x3 = first[j3] - first[j2];
                    first[j2] += first[j3];
                    first[j1] = value_type(
                        real(x1) - imag(x3),
                        imag(x1) + real(x3)
                        );
                    first[j3] = value_type(
                        real(x1) + imag(x3),
                        imag(x1) - real(x3)
                        );
                }
            }
            if (m == size) {
                continue;
            }
            difference_type irev = size >> 1;
            elem_type w1r = sprout::cos(theta * irev);
            for (difference_type k = mq; k >= 1; k >>= 2) {
                for (difference_type j = m + mq - k; j < m + mq - (k >> 1); ++j) {
                    difference_type j1 = j + mq;
                    difference_type j2 = j1 + mq;
                    difference_type j3 = j2 + mq;
                    value_type x1 = first[j] - first[j1];
                    first[j] += first[j1];
                    value_type x3 = first[j3] - first[j2];
                    first[j2] += first[j3];
                    elem_type x0r = real(x1) - imag(x3);
                    elem_type x0i = imag(x1) + real(x3);
                    first[j1] = value_type(
                        w1r * (x0r + x0i),
                        w1r * (x0i - x0r)
                        );
                    x0r = real(x1) + imag(x3);
                    x0i = imag(x1) - real(x3);
                    first[j3] = value_type(
                        w1r * (-x0r + x0i),
                        w1r * (-x0i - x0r)
                        );
                }
            }
            for (difference_type i = 2 * m; i < size; i += m) {
                for (difference_type k = size >> 1; k > (irev ^= k); k >>= 1)
                    ;
                elem_type w1r = sprout::cos(theta * irev);
                elem_type w1i = sprout::sin(theta * irev);
                elem_type w3r = sprout::cos(theta * 3 * irev);
                elem_type w3i = sprout::sin(theta * 3 * irev);
                for (difference_type k = mq; k >= 1; k >>= 2) {
                    for (difference_type j = i + mq - k; j < i + mq - (k >> 1); ++j) {
                        difference_type j1 = j + mq;
                        difference_type j2 = j1 + mq;
                        difference_type j3 = j2 + mq;
                        value_type x1 = first[j] - first[j1];
                        first[j] += first[j1];
                        value_type x3 = first[j3] - first[j2];
                        first[j2] += first[j3];
                        elem_type x0r = real(x1) - imag(x3);
                        elem_type x0i = imag(x1) + real(x3);
                        first[j1] = value_type(
                            w1r * x0r - w1i * x0i,
                            w1r * x0i + w1i * x0r
                            );
                        x0r = real(x1) + imag(x3);
                        x0i = imag(x1) - real(x3);
                        first[j3] = value_type(
                            w3r * x0r - w3i * x0i,
                            w3r * x0i + w3i * x0r
                            );
                    }
                }
            }
        }
        // radix 2 butterflies
        difference_type mq = size >> 1;
        for (difference_type k = mq; k >= 1; k >>= 2) {
            for (difference_type j = mq - k; j < mq - (k >> 1); ++j) {
                difference_type j1 = mq + j;
                value_type x0 = first[j] - first[j1];
                first[j] += first[j1];
                first[j1] = x0;
            }
        }
    }
}   // namespace sprout

#endif  // #ifndef SPROUT_NUMERIC_FFT_FFT_HPP


使用例はこのようになる。

#include <iostream>
#include <fstream>
#include <sprout/array.hpp>
#include <sprout/pit.hpp>
#include <sprout/complex.hpp>
#include <sprout/range/numeric/dft.hpp>
#include <sprout/range/numeric/fft.hpp>
#include <sprout/range/adaptor.hpp>
#include <sprout/range/algorithm.hpp>
#include <sprout/functional.hpp>

template<typename Elem, typename Traits, typename InputRange>
std::basic_ostream<Elem, Traits>& output_plot(std::basic_ostream<Elem, Traits>& os, InputRange const& range) {
    os << std::fixed;
    int x = 0;
    for (auto const& e : range) {
        os << x++ << ',' << e << '\n';
    }
    return os;
}
template<typename Elem, typename Traits, typename InputRange>
std::basic_ostream<Elem, Traits>& output_plot_real(std::basic_ostream<Elem, Traits>& os, InputRange const& range) {
    os << std::fixed;
    int x = 0;
    for (auto const& e : range) {
        os << x++ << ',' << real(e) << '\n';
    }
    return os;
}


SPROUT_CONSTEXPR std::size_t size = 256;
typedef sprout::array<sprout::complex<double>, size> container_t;
typedef sprout::array<double, size> spectrum_t;

SPROUT_CXX14_CONSTEXPR container_t
fft(container_t const& src_data) {
    using namespace sprout;
    auto result = src_data;
    range::fft(result);
    return result;
}

SPROUT_CXX14_CONSTEXPR container_t
ifft(container_t const& src_data) {
    using namespace sprout;
    auto result = src_data;
    range::ifft(result);
    return result;
}

SPROUT_CXX14_CONSTEXPR spectrum_t
amplitude_spectrum(container_t const& dft_data) {
    using namespace sprout;
    auto result = spectrum_t();
    range::amplitude_spectrum(dft_data, sprout::begin(result));
    return result;
}

SPROUT_CXX14_CONSTEXPR spectrum_t
phase_spectrum(container_t const& dft_data) {
    using namespace sprout;
    auto result = spectrum_t();
    range::phase_spectrum(dft_data, sprout::begin(result));
    return result;
}

int main() {
    using namespace sprout;
    namespace adp = sprout::adaptors;

    // 周波数 10, 25, 35 の正弦波の重ね合わせを生成
    SPROUT_CXX14_CONSTEXPR container_t src_data
        = adp::sinusoidal(10. / ::size, 10000.)
        | adp::transformed(adp::sinusoidal(25. / ::size, 5000.), plus<double>())
        | adp::transformed(adp::sinusoidal(35. / ::size, 7500.), plus<double>())
        | adp::copied
        ;
    {
        std::ofstream os("src.txt");
        output_plot_real(os, src_data);
    }

    // DFT(離散フーリエ変換)
    SPROUT_CXX14_CONSTEXPR auto dft_data = ::fft(src_data);

    // IDFT(逆離散フーリエ変換)
    SPROUT_CXX14_CONSTEXPR auto idft_data = ::ifft(dft_data);
    {
        std::ofstream os("dft_idft.txt");
        output_plot_real(os, idft_data);
    }

    // 周波数スペクトルに変換
    SPROUT_CXX14_CONSTEXPR auto amp_spec_data = ::amplitude_spectrum(dft_data);
    {
        std::ofstream os("amp_spec.txt");
        output_plot(os, amp_spec_data);
    }

    // 位相スペクトルに変換
    SPROUT_CXX14_CONSTEXPR auto pha_spec_data = ::phase_spectrum(dft_data);
    {
        std::ofstream os("pha_spec.txt");
        output_plot(os, pha_spec_data);
    }
}

出力は最初と同様なので割愛する。


このように、C++14 では、C++11 で実装できなかったコンパイルアルゴリズムを実装できるようになった。
これが進歩というものである。

コンパイルBrainfuckコンパイラ (x86)

多くのプログラマが一度はBrainfuck処理系を実装したことがあるだろう。
C++11 constexpr の制約下でも、「Brainfuckインタプリタ」を実装することは可能である。

/*=============================================================================
  Copyright (c) 2011-2014 Bolero MURAKAMI
  https://github.com/bolero-MURAKAMI/Sprout

  Distributed under the Boost Software License, Version 1.0. (See accompanying
  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#ifndef SPROUT_BRAINFUCK_BRAINFUCK_HPP
#define SPROUT_BRAINFUCK_BRAINFUCK_HPP

#include <iterator>
#include <stdexcept>
#include <sprout/config.hpp>
#include <sprout/workaround/std/cstddef.hpp>
#include <sprout/array/array.hpp>
#include <sprout/pit/pit.hpp>
#include <sprout/iterator/operation.hpp>
#include <sprout/iterator/value_iterator.hpp>
#include <sprout/container/traits.hpp>
#include <sprout/container/functions.hpp>
#include <sprout/algorithm/fixed/results.hpp>
#include <sprout/algorithm/fixed/copy.hpp>
#include <sprout/operation/fixed/set.hpp>
#include <sprout/detail/char_literal.hpp>
#include HDR_ALGORITHM_MIN_MAX_SSCRISK_CEL_OR_SPROUT

namespace sprout {
    namespace brainfuck {
        namespace detail {
            template<typename InputIterator>
            inline SPROUT_CONSTEXPR InputIterator
            find_scope_end(InputIterator first, std::size_t count = 0) {
                typedef typename std::iterator_traits<InputIterator>::value_type value_type;
                return *first == SPROUT_CHAR_LITERAL('[', value_type) ? sprout::brainfuck::detail::find_scope_end(sprout::next(first), count + 1)
                    : *first == SPROUT_CHAR_LITERAL(']', value_type) ? count == 0
                        ? first
                        : sprout::brainfuck::detail::find_scope_end(sprout::next(first), count - 1)
                    : sprout::brainfuck::detail::find_scope_end(sprout::next(first), count)
                    ;
            }

            template<typename BidirectionalIterator>
            inline SPROUT_CONSTEXPR BidirectionalIterator
            find_scope_start(BidirectionalIterator first, std::size_t count = 0) {
                typedef typename std::iterator_traits<BidirectionalIterator>::value_type value_type;
                return *first == SPROUT_CHAR_LITERAL(']', value_type) ? sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count + 1)
                    : *first == SPROUT_CHAR_LITERAL('[', value_type) ? count == 0
                        ? first
                        : sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count - 1)
                    : sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count)
                    ;
            }

            template<typename InputIterator>
            inline SPROUT_CONSTEXPR bool
            is_well_formed(InputIterator first, InputIterator last, std::size_t count = 0) {
                typedef typename std::iterator_traits<InputIterator>::value_type value_type;
                return first == last ? count == 0
                    : *first == SPROUT_CHAR_LITERAL('[', value_type)
                        ? sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count + 1)
                    : *first == SPROUT_CHAR_LITERAL(']', value_type)
                        ? count != 0 && sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count - 1)
                    : sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count)
                    ;
            }

            template<
                typename BidirectionalIteratorSource, typename Output, typename InputIteratorInput,
                typename Buffer, typename OutputBuffer
            >
            inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type
            exec_impl(
                BidirectionalIteratorSource first, BidirectionalIteratorSource last,
                Output const& output, InputIteratorInput in_first, InputIteratorInput in_last,
                Buffer const& buffer, OutputBuffer const& out_buffer, std::size_t pos = 0, std::size_t out_pos = 0
                )
            {
                typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type;
                typedef typename sprout::container_traits<OutputBuffer>::value_type out_value_type;
                return first == last
                    ? sprout::fixed::copy(
                        sprout::begin(out_buffer),
                        sprout::next(sprout::begin(out_buffer), NS_SSCRISK_CEL_OR_SPROUT::min(out_pos, sprout::size(out_buffer))),
                        output
                        )
                    : *first == SPROUT_CHAR_LITERAL('>', value_type)
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            buffer, out_buffer, pos + 1, out_pos
                            )
                    : *first == SPROUT_CHAR_LITERAL('<', value_type)
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            buffer, out_buffer, pos - 1, out_pos
                            )
                    : *first == SPROUT_CHAR_LITERAL('+', value_type)
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            sprout::fixed::set(buffer, pos, value_type(buffer.at(pos) + 1)), out_buffer, pos, out_pos
                            )
                    : *first == SPROUT_CHAR_LITERAL('+', value_type)
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            sprout::fixed::set(buffer, pos, value_type(buffer.at(pos) - 1)), out_buffer, pos, out_pos
                            )
                    : *first == SPROUT_CHAR_LITERAL('.', value_type) ? out_pos != out_buffer.size()
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            buffer, sprout::fixed::set(out_buffer, out_pos, out_value_type(buffer.at(pos))), pos, out_pos + 1
                            )
                        : throw std::out_of_range("output out of range")
                    : *first == SPROUT_CHAR_LITERAL(',', value_type) ? in_first != in_last
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, sprout::next(in_first), in_last,
                            sprout::fixed::set(buffer, pos, value_type(*in_first)), out_buffer, pos, out_pos
                            )
                        : throw std::out_of_range("input out of range")
                    : *first == SPROUT_CHAR_LITERAL('[', value_type) ? buffer.at(pos) == 0
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(sprout::brainfuck::detail::find_scope_end(sprout::next(first))), last, output, in_first, in_last,
                            buffer, out_buffer, pos, out_pos
                            )
                        : sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            buffer, out_buffer, pos, out_pos
                            )
                    : *first == SPROUT_CHAR_LITERAL(']', value_type) ? buffer.at(pos) != 0
                        ? sprout::brainfuck::detail::exec_impl(
                            sprout::next(sprout::brainfuck::detail::find_scope_start(sprout::prev(first))), last, output, in_first, in_last,
                            buffer, out_buffer, pos, out_pos
                            )
                        : sprout::brainfuck::detail::exec_impl(
                            sprout::next(first), last, output, in_first, in_last,
                            buffer, out_buffer, pos, out_pos
                            )
                    : sprout::brainfuck::detail::exec_impl(
                        sprout::next(first), last, output, in_first, in_last,
                        buffer, out_buffer, pos, out_pos
                        )
                    ;
            }
        }   // namespace detail

        //
        // exec
        //
        template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource, typename Output, typename InputIteratorInput>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type
        exec(
            BidirectionalIteratorSource first, BidirectionalIteratorSource last,
            Output const& output, InputIteratorInput in_first, InputIteratorInput in_last
            )
        {
            typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type;
            typedef sprout::container_traits<Output> out_traits;
            return sprout::brainfuck::detail::exec_impl(
                first, last, output, in_first, in_last,
                sprout::array<value_type, BufferSize>{{}}, sprout::array<typename out_traits::value_type, out_traits::static_size>{{}}
                );
        }
        template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource, typename Output>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type
        exec(
            BidirectionalIteratorSource first, BidirectionalIteratorSource last,
            Output const& output
            )
        {
            typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type;
            return sprout::brainfuck::exec<BufferSize>(
                first, last, output, sprout::value_iterator<value_type>(value_type()), sprout::value_iterator<value_type>()
                );
        }
        template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<
            sprout::array<typename std::iterator_traits<BidirectionalIteratorSource>::value_type, BufferSize>
        >::type
        exec(
            BidirectionalIteratorSource first, BidirectionalIteratorSource last
            )
        {
            typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type;
            return sprout::brainfuck::exec<BufferSize>(
                first, last, sprout::pit<sprout::array<value_type, BufferSize> >()
                );
        }

        //
        // exec_range
        //
        template<std::size_t BufferSize = 32, typename BidirectionalRangeSource, typename Output, typename InputRangeInput>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type
        exec_range(BidirectionalRangeSource const& source, Output const& output, InputRangeInput const& input) {
            return sprout::brainfuck::exec<BufferSize>(
                sprout::begin(source), sprout::end(source), output, sprout::begin(input), sprout::end(input)
                );
        }
        template<std::size_t BufferSize = 32, typename BidirectionalRangeSource, typename Output>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type
        exec_range(BidirectionalRangeSource const& source, Output const& output) {
            return sprout::brainfuck::exec<BufferSize>(
                sprout::begin(source), sprout::end(source), output
                );
        }
        template<std::size_t BufferSize = 32, typename BidirectionalRangeSource>
        inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<
            sprout::array<typename sprout::container_traits<BidirectionalRangeSource>::value_type, BufferSize>
        >::type
        exec_range(BidirectionalRangeSource const& source) {
            return sprout::brainfuck::exec<BufferSize>(
                sprout::begin(source), sprout::end(source)
                );
        }

        //
        // is_well_formed
        //
        template<typename InputIterator>
        inline SPROUT_CONSTEXPR bool
        is_well_formed(InputIterator first, InputIterator last) {
            return sprout::brainfuck::detail::is_well_formed(first, last);
        }
    }   // namespace brainfuck
}   // namespace sprout

#endif  // #ifndef SPROUT_BRAINFUCK_BRAINFUCK_HPP


しかし「Brainfuckコンパイラ」となると、C++11 constexpr では困難なうえに、実装できたとしても非常に非効率な処理にならざるをえない。
実際、筆者も以前一度実装しようとして挫折している。


C++14 では、コンパイルBrainfuckコンパイラも容易に実装できる。

#include <cstddef>
#include <cstdint>
#include <iterator>
#include <iostream>
#include <fstream>
#include <sprout/config.hpp>
#include <sprout/endian_traits.hpp>
#include <sprout/string.hpp>
#include <sprout/array.hpp>
#include <sprout/sub_array.hpp>
#include <sprout/container/functions.hpp>
#include <sprout/range/algorithm.hpp>
#include <sprout/preprocessor/stringize.hpp>
#include <sprout/assert.hpp>


//
// PE関連の構造体
// <windows.h> からコピペ
//
typedef unsigned long       DWORD;
typedef int                 BOOL;
typedef unsigned char       BYTE;
typedef unsigned short      WORD;


//
// File header format.
//

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


//
// Directory format.
//

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

//
// Optional header format.
//

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;


//
// Section header format.
//

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
//    union {
//            DWORD   PhysicalAddress;
//            DWORD   VirtualSize;
//    } Misc;
    struct {
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


//
// 出力バイナリサイズ
//
#ifndef BRAINFUCK_BINARY_SIZE
#   define BRAINFUCK_BINARY_SIZE (16 * 1024)
#endif

//
// ループ制限
//
#ifndef BRAINFUCK_LOOP_LIMIT
#   define BRAINFUCK_LOOP_LIMIT 256
#endif

//
// 入力ファイル名
//
#ifndef BRAINFUCK_SOURCE_FILE
#   define BRAINFUCK_SOURCE_FILE a.bf
#endif


//
// 定数
//
SPROUT_STATIC_CONSTEXPR std::size_t bin_size = BRAINFUCK_BINARY_SIZE;

SPROUT_STATIC_CONSTEXPR std::size_t loop_limit = BRAINFUCK_LOOP_LIMIT;

SPROUT_STATIC_CONSTEXPR auto source = sprout::to_string(
#   include SPROUT_PP_STRINGIZE(BRAINFUCK_SOURCE_FILE)
    );

SPROUT_STATIC_CONSTEXPR std::int32_t addr_putchar = 0x00405044;
SPROUT_STATIC_CONSTEXPR std::int32_t addr_getchar = addr_putchar + 4;
SPROUT_STATIC_CONSTEXPR std::int32_t addr_buf = 0x00406000;


//
// 出力用関数
//
template<typename OutputIterator, typename T>
SPROUT_CXX14_CONSTEXPR std::size_t
write_bytes(OutputIterator& out, T const& val) {
    typedef sprout::little_endian_traits<T> traits;
    for (std::size_t i = 0; i != traits::size(); ++i) {
        *out++ = sprout::little_endian_traits<T>::get_byte(val, i);
    }
    return traits::size();
}
template<typename OutputIterator, typename T, std::size_t N>
SPROUT_CXX14_CONSTEXPR std::size_t
write_bytes(OutputIterator& out, T const(& vals)[N]) {
    typedef sprout::little_endian_traits<T> traits;
    for (std::size_t i = 0; i != N; ++i) {
        ::write_bytes(out, vals[i]);
    }
    return traits::size() * N;
}
template<typename OutputIterator, typename Head, typename... Tail>
SPROUT_CXX14_CONSTEXPR std::size_t
write_bytes(OutputIterator& out, Head const& head, Tail const&... tail) {
    return ::write_bytes(out, head) + ::write_bytes(out, tail...);
}

template<typename OutputIterator>
SPROUT_CXX14_CONSTEXPR std::size_t
write_pe_header(OutputIterator& out, std::size_t n = 0) {
    SPROUT_CONSTEXPR unsigned char stub[] = {
        // 00-3b: DOS Header
        'M',  'Z',  0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x00,
        0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // 3c-3f: Pointer to PE Header (=80)
        0x80, 0x00, 0x00, 0x00,
        // 40-7f: DOS Stub
        0xba, 0x10, 0x00, 0x0e, 0x1f, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x90, 0x90,
        'T', 'h', 'i', 's', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'c', 'a', 'n',
        'n', 'o', 't', ' ', 'b', 'e', ' ', 'r', 'u', 'n', ' ', 'i', 'n', ' ', 'D', 'O',
        'S', ' ', 'm', 'o', 'd', 'e', '.', '\r', '\n', '$', 0, 0, 0, 0, 0, 0,
        // 80-83: PE Signature
        'P', 'E', 0, 0
    };
    n += ::write_bytes(
        out,
        stub
        );

    SPROUT_CONSTEXPR ::IMAGE_FILE_HEADER coff = {
        0x014c,                             // Machine
        3,                                  // NumberOfSections
        0,                                  // TimeDateStamp
        0,                                  // PointerToSymbolTable
        0,                                  // NumberOfSymbols
        sizeof(::IMAGE_OPTIONAL_HEADER32),  // SizeOfOptionalHeader
        0x030f                              // Characteristics
    };
    n += ::write_bytes(
        out,
        coff.Machine,
        coff.NumberOfSections,
        coff.TimeDateStamp,
        coff.PointerToSymbolTable,
        coff.NumberOfSymbols,
        coff.SizeOfOptionalHeader,
        coff.Characteristics
        );

    SPROUT_CONSTEXPR ::IMAGE_OPTIONAL_HEADER32 opt = {
        0x010b,         // Magic
        6, 0,           // MajorLinkerVersion, MinorLinkerVersion
        ::bin_size,     // SizeOfCode
        0,              // SizeOfInitializedData
        65536,          // SizeOfUninitializedData
        0x1000,         // AddressOfEntryPoint
        0x1000,         // BaseOfCode
        0x6000,         // BaseOfData
        0x00400000,     // ImageBase
        0x1000,         // SectionAlignment
        0x0200,         // FileAlignment
        4, 0,           // MajorOperatingSystemVersion, MinorOperatingSystemVersion
        0, 0,           // MajorImageVersion, MinorImageVersion
        4, 0,           // MajorSubsystemVersion, MinorSubsystemVersion
        0,              // Win32VersionValue
        0x16000,        // SizeOfImage
        0x200,          // SizeOfHeaders
        0,              // CheckSum
        3,              // Subsystem
        0,              // DllCharacteristics
        1024 * 1024,    // SizeOfStackReserve
        8 * 1024,       // SizeOfStackCommit
        1024 * 1024,    // SizeOfHeapReserve
        4 * 1024,       // SizeOfHeapCommit
        0,              // LoaderFlags
        16,             // NumberOfRvaAndSizes
        {
            {},
            {
                0x5000, // VirtualAddress (import table)
                100     // Size
            },
            {}
        }
    };
    n += ::write_bytes(
        out,
        opt.Magic,
        opt.MajorLinkerVersion, opt.MinorLinkerVersion,
        opt.SizeOfCode,
        opt.SizeOfInitializedData,
        opt.SizeOfUninitializedData,
        opt.AddressOfEntryPoint,
        opt.BaseOfCode,
        opt.BaseOfData,
        opt.ImageBase,
        opt.SectionAlignment,
        opt.FileAlignment,
        opt.MajorOperatingSystemVersion, opt.MinorOperatingSystemVersion,
        opt.MajorImageVersion, opt.MinorImageVersion,
        opt.MajorSubsystemVersion, opt.MinorSubsystemVersion,
        opt.Win32VersionValue,
        opt.SizeOfImage,
        opt.SizeOfHeaders,
        opt.CheckSum,
        opt.Subsystem,
        opt.DllCharacteristics,
        opt.SizeOfStackReserve,
        opt.SizeOfStackCommit,
        opt.SizeOfHeapReserve,
        opt.SizeOfHeapCommit,
        opt.LoaderFlags,
        opt.NumberOfRvaAndSizes,
        opt.DataDirectory[0].VirtualAddress, opt.DataDirectory[0].Size,
        opt.DataDirectory[1].VirtualAddress, opt.DataDirectory[1].Size
        );
    for (std::size_t i = 2; i != IMAGE_NUMBEROF_DIRECTORY_ENTRIES; ++i) {
        n += ::write_bytes(
            out,
            opt.DataDirectory[i].VirtualAddress, opt.DataDirectory[i].Size
            );
    }

    SPROUT_CONSTEXPR ::IMAGE_SECTION_HEADER sects[3] = {
        {
            ".text",        // Name
            {::bin_size},   // Misc.VirtualSize
            0x1000,         // VirtualAddress
            ::bin_size,     // SizeOfRawData
            0x400,          // PointerToRawData
            0,              // PointerToRelocations
            0,              // PointerToLinenumbers
            0,              // NumberOfRelocations
            0,              // NumberOfLinenumbers
            0x60500060      // Characteristics
        },
        {
            ".idata",       // Name
            {100},          // Misc.VirtualSize
            0x5000,         // VirtualAddress
            512,            // SizeOfRawData
            0x200,          // PointerToRawData
            0,              // PointerToRelocations
            0,              // PointerToLinenumbers
            0,              // NumberOfRelocations
            0,              // NumberOfLinenumbers
            0xc0300040      // Characteristics
        },
        {
            ".bss",         // Name
            {65536},        // Misc.VirtualSize
            0x6000,         // VirtualAddress
            0,              // SizeOfRawData
            0,              // PointerToRawData
            0,              // PointerToRelocations
            0,              // PointerToLinenumbers
            0,              // NumberOfRelocations
            0,              // NumberOfLinenumbers
            0xc0400080      // Characteristics
        }
    };
    for (std::size_t i = 0; i != 3; ++i) {
        n += ::write_bytes(
            out,
            sects[i].Name,
            sects[i].Misc.VirtualSize,
            sects[i].VirtualAddress,
            sects[i].SizeOfRawData,
            sects[i].PointerToRawData,
            sects[i].PointerToRelocations,
            sects[i].PointerToLinenumbers,
            sects[i].NumberOfRelocations,
            sects[i].NumberOfLinenumbers,
            sects[i].Characteristics
            );
    }

    for (; n != 0x200; ) {
        n += ::write_bytes(
            out,
            (unsigned char)0
            );
    }
    return n;
}

template<typename OutputIterator>
SPROUT_CXX14_CONSTEXPR std::size_t
write_idata(OutputIterator& out, std::size_t n = 0) {
    SPROUT_CONSTEXPR int idt[] = {
        // IDT 1
        0x5028, 0, 0, 0x5034, 0x5044,
        // IDT (終端)
        0, 0, 0, 0, 0
    };
    n += ::write_bytes(
        out,
        idt
        );

    SPROUT_CONSTEXPR int ilt_iat[]  = {
        0x5050, 0x505a, 0
    };

    // ILT
    n += ::write_bytes(
        out,
        ilt_iat
        );
    n += ::write_bytes(
        out,
        "msvcrt.dll\0\0\0\0\0"
        );

    // IAT
    n += ::write_bytes(
        out,
        ilt_iat
        );
    n += ::write_bytes(
        out,
        (std::int16_t)0,
        "putchar",
        (std::int16_t)0,
        "getchar"
        );

    for (; n != 0x400; ) {
        n += ::write_bytes(
            out,
            (unsigned char)0
            );
    }
    return n;
}

//
// コンパイル
//
template<std::size_t LoopLimit = 256, typename InputIterator, typename RandomAccessIterator>
SPROUT_CXX14_CONSTEXPR std::size_t
compile(InputIterator first, InputIterator last, RandomAccessIterator& out_first) {
    sprout::array<int, LoopLimit> loops {{}};   // ループを保存するスタック
    auto loop_first = sprout::begin(loops);
    auto loop_last = sprout::end(loops);
    auto loop = loop_first;

    int idx = 0;
    auto out = out_first;

    idx += ::write_bytes(
        out,
        (unsigned char)0x31, (unsigned char)0xc9,                                                       // xor ecx, ecx
        (unsigned char)0x57,                                                                            // push edi
        (unsigned char)0xbf, ::addr_buf                                                                 // mov edi, addr_buf
        );

    for (; first != last; ++first) {
        switch (*first) {
            case '>':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0x66, (unsigned char)0x41                                            // inc cx
                    );
                break;
            case '<':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0x66, (unsigned char)0x49                                            // dec cx
                    );
                break;
            case '+':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0xfe, (unsigned char)0x04, (unsigned char)0x0f                       // inc byte [edi+ecx]
                    );
                break;
            case '-':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0xfe, (unsigned char)0x0c, (unsigned char)0x0f                       // dec byte [edi+ecx]
                    );
                break;
            case '.':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0x51,                                                                // push ecx
                    (unsigned char)0x0f, (unsigned char)0xb6, (unsigned char)0x04, (unsigned char)0x0f, // movzx eax,byte [edi+ecx]
                    (unsigned char)0x50,                                                                // push eax
                    (unsigned char)0xa1, ::addr_putchar,                                                // mov eax, [addr_putchar]
                    (unsigned char)0xff, (unsigned char)0xd0,                                           // call eax
                    (unsigned char)0x58,                                                                // pop eax
                    (unsigned char)0x59                                                                 // pop ecx
                    );
                break;
            case ',':
                idx += ::write_bytes(
                    out,
                    (unsigned char)0x51,                                                                // push ecx
                    (unsigned char)0xa1, ::addr_getchar,                                                // mov eax, [addr_getchar]
                    (unsigned char)0xff, (unsigned char)0xd0,                                           // call eax
                    (unsigned char)0x59,                                                                // pop ecx
                    (unsigned char)0x88, (unsigned char)0x04, (unsigned char)0x0f                       // mov [edi+ecx],al
                    );
                break;
            case '[':
                SPROUT_ASSERT_MSG(loop != loop_last, "loop overflow");
                *loop++ = idx;  // インデックスをスタックに積む
                idx += ::write_bytes(
                    out,
                    (unsigned char)0x80, (unsigned char)0x3c, (unsigned char)0x0f, (unsigned char)0x00, // cmp byte [edi+ecx],0
                    (unsigned char)0x0f, (unsigned char)0x84,                                           // jz 対応する]の直後
                    (unsigned char)0xde, (unsigned char)0xad, (unsigned char)0xbe, (unsigned char)0xef  // (アドレスは後で決定)
                    );
                break;
            case ']':
            {
                SPROUT_ASSERT_MSG(loop != loop_first, "missing '['");
                int idx_loop = *--loop;
                idx += ::write_bytes(
                    out,
                    (unsigned char)0xe9, (std::int32_t)(idx_loop - (idx + 5))                           // jmp idx_loop
                    );
                auto out_loop = out_first + (idx_loop + 6);
                ::write_bytes(
                    out_loop,
                    (std::int32_t)(idx - (idx_loop + 10))                                               // (アドレス)
                    );
                break;
            }
        }
        SPROUT_ASSERT_MSG(idx <= (int)::bin_size - 32, "buffer overflow");
    }

    SPROUT_ASSERT_MSG(loop == loop_first, "missing ']'");

    // 終了処理
    idx += ::write_bytes(
        out,
        (unsigned char)0x5f,                                                                            // pop edi
        (unsigned char)0x31, (unsigned char)0xc0,                                                       // xor eax, eax
        (unsigned char)0xc3                                                                             // ret
        );

    return idx;
}

//
// ビルド
//
template<std::size_t LoopLimit = 256, typename InputIterator, typename RandomAccessIterator>
SPROUT_CXX14_CONSTEXPR std::size_t
build(InputIterator first, InputIterator last, RandomAccessIterator& out) {
    std::size_t n = 0;
    n += ::write_pe_header(out, n);                 // ヘッダ出力
    n += ::write_idata(out, n);                     // .idataセクション出力
    n += ::compile<LoopLimit>(first, last, out);    // コンパイル
    return n;
}


SPROUT_CXX14_CONSTEXPR sprout::sub_array<sprout::array<unsigned char, ::bin_size> >
build_brainfuck() {
    sprout::array<unsigned char, ::bin_size> bin{{}};
    auto out = sprout::begin(bin);
    auto n = ::build<::loop_limit>(sprout::begin(::source), sprout::end(::source), out);
    return sprout::sub_copy(bin, 0, n);
}

int main(int argc, char* argv[]) {
    SPROUT_CXX14_CONSTEXPR auto bin = ::build_brainfuck();

    char const* filename = argc >= 2 ? argv[1]
        : "a.exe"
        ;
    std::ofstream ofs(filename);
    if (!ofs) {
        std::cout
            << "could not open file" << std::endl
            ;
        return -1;
    }
    sprout::range::copy(bin, std::ostreambuf_iterator<char>(ofs));
}

このとおりだ。


実装にあたっては 七誌 氏による実行時Brainfuckコンパイラのコードを参考にした。


C++14 constexpr の制約を満たす上では、出力先を FILE* ではなく OutputIterator で渡すようにしたり、バイト毎に(C++処理系のエンディアンにかかわらず)リトルエンディアンで出力されるようにした。
また、非リテラル型である std::vector 等も使わないようにした。
その他関数の切り分け方などいくつかの相違点があるが、オペコード生成とPEヘッダ付加の処理の流れは実行時コンパイラとほとんど変わりない。
見てのとおり、非常に明快でわかりやすいコードになっている。


C++14 constexpr の制約を満たすよう実装したことによる、メリットと言えることがいくつかある。


これらのメリットは、単にコンパイル時処理の範疇だけに留まるものではない。
総称的で、変更に強く、副作用にまつわるバグのリスクを低減する、モダンなプログラミングスタイルの指針のいくつかを満足するものだ。


とはいえデメリットもある。
C++14 でも相変わらず定数式で動的オブジェクトを扱うことができないため、コンパイル時処理の場合は固定長バッファを用いるほかない。
しかしながら、インタフェースを工夫することで、array といった具体的な型が低水準の処理に表れないようにすることはできる。


さて、このコンパイルコンパイラBrainfuckソースをコンパイルしたバイナリをWindows上で実行した結果をいろいろ見てみる。
なお、Brainfuckソースは C++ の文字列リテラルとして記述する必要がある。これは、#include でソースをそのまま読み込めるようにするためだ。

  • hello.bf
// Hello, world!
"+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-."
"------------.<++++++++.--------.+++.------.--------.>+."
  • 出力
Hello, world!



  • echo.bf (入力した文字列をそのまま出力する)
// echo
"+[>,.<]"
  • 入力
foobar
  • 出力
foobar



// FizzBuzz
"++++++++++++[->++++++>+++++++++>+++++>++++++++++>++++++++++>+++>>>>>>++++++++<<<<<<<<<<<<]>-->--->++"
"++++>--->++>---->>>>+++>+++++>++++[>>>+[-<<[->>+>+<<<]>>>[-<<<+>>>]+<[[-]>-<<[->+>+<<]>>[-<<+>>]+<[["
"-]>-<<<+>->]>[-<<<--------->+++++++++>>>>>+<<<]<]>[-<+++++++[<<+++++++>>-]<++++++++>>]>>>]<<<<<<[<<<"
"<]>-[-<<+>+>]<[->+<]+<[[-]>-<]>[->+++<<<<<<<<<.>.>>>..>>+>>]>>-[-<<<+>+>>]<<[->>+<<]+<[[-]>-<]>[->>+"
"++++<<<<<<<<.>.>..>>+>>]<+<[[-]>-<]>[->>>>>[>>>>]<<<<[.<<<<]<]<<.>>>>>>-]"
  • 出力
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz



// Quine
"-"
">++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++"
">+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+>+>>+++>>>>+++>>>+++"
">+>>>>>>>++>+++>+++>+>>+++>+++>+>+++>+>+++>+>++>+++>>>+"
">+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++"
">+++>+>+++>+>++>+++>++>>+>+>++>+++>+>+>>+++>>>+++>+>>>++"
">+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>"
"+[[>>+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]"
">>>[>]+++>+"
"[+[<++++++++++++++++>-]<++++++++++.<]"
  • 出力
->++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++>+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+>+>>+++>>>>+++>>>+++>+>>>>>>>++>+++>+++>+>>+++>+++>+>+++>+>+++>+>++>+++>>>+>+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++>+++>+>+++>+>++>+++>++>>+>+>++>+++>+>+>>+++>>>+++>+>>>++>+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>+[[>>+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]>>>[>]+++>+[+[<++++++++++++++++>-]<++++++++++.<]



  • bottles.bf (99本のボトルが0本になるまで飲み干していくカウントダウン)
// 99 Bottles of Beer
">+++++++++[<+++++++++++>-]<[>[-]>[-]<<[>+>+<<-]>>[<<+>>-]>>>"
"[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<"
"-]<<-<-]+++++++++>[<->-]>>+>[<[-]<<+>>>-]>[-]+<<[>+>-<<-]<<<"
"[>>+>+<<<-]>>>[<<<+>>>-]>[<+>-]<<-[>[-]<[-]]>>+<[>[-]<-]<+++"
"+++++[<++++++<++++++>>-]>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-"
"]<<<<<<.>>[-]>[-]++++[<++++++++>-]<.>++++[<++++++++>-]<++.>+"
"++++[<+++++++++>-]<.><+++++..--------.-------.>>[>>+>+<<<-]>"
">>[<<<+>>>-]<[<<<<++++++++++++++.>>>>-]<<<<[-]>++++[<+++++++"
"+>-]<.>+++++++++[<+++++++++>-]<--.---------.>+++++++[<------"
"---->-]<.>++++++[<+++++++++++>-]<.+++..+++++++++++++.>++++++"
"++[<---------->-]<--.>+++++++++[<+++++++++>-]<--.-.>++++++++"
"[<---------->-]<++.>++++++++[<++++++++++>-]<++++.-----------"
"-.---.>+++++++[<---------->-]<+.>++++++++[<+++++++++++>-]<-."
">++[<----------->-]<.+++++++++++..>+++++++++[<---------->-]<"
"-----.---.>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>>+++"
"+[<++++++>-]<--.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<."
"><+++++..--------.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++"
"++++++++++++.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<++"
"+++++++>-]<--.---------.>+++++++[<---------->-]<.>++++++[<++"
"+++++++++>-]<.+++..+++++++++++++.>++++++++++[<---------->-]<"
"-.---.>+++++++[<++++++++++>-]<++++.+++++++++++++.++++++++++."
"------.>+++++++[<---------->-]<+.>++++++++[<++++++++++>-]<-."
"-.---------.>+++++++[<---------->-]<+.>+++++++[<++++++++++>-"
"]<--.+++++++++++.++++++++.---------.>++++++++[<---------->-]"
"<++.>+++++[<+++++++++++++>-]<.+++++++++++++.----------.>++++"
"+++[<---------->-]<++.>++++++++[<++++++++++>-]<.>+++[<----->"
"-]<.>+++[<++++++>-]<..>+++++++++[<--------->-]<--.>+++++++[<"
"++++++++++>-]<+++.+++++++++++.>++++++++[<----------->-]<++++"
".>+++++[<+++++++++++++>-]<.>+++[<++++++>-]<-.---.++++++.----"
"---.----------.>++++++++[<----------->-]<+.---.[-]<<<->[-]>["
"-]<<[>+>+<<-]>>[<<+>>-]>>>[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]"
">[<+>-]>[<<++++++++++>>>+<-]<<-<-]+++++++++>[<->-]>>+>[<[-]<"
"<+>>>-]>[-]+<<[>+>-<<-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<>>[<+>-]<"
"<-[>[-]<[-]]>>+<[>[-]<-]<++++++++[<++++++<++++++>>-]>>>[>+>+"
"<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>[-]>[-]++++[<++++++++>"
"-]<.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.><+++++..---"
"-----.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++++++++++++++"
".>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<+++++++++>-]<-"
"-.---------.>+++++++[<---------->-]<.>++++++[<+++++++++++>-]"
"<.+++..+++++++++++++.>++++++++[<---------->-]<--.>+++++++++["
"<+++++++++>-]<--.-.>++++++++[<---------->-]<++.>++++++++[<++"
"++++++++>-]<++++.------------.---.>+++++++[<---------->-]<+."
">++++++++[<+++++++++++>-]<-.>++[<----------->-]<.+++++++++++"
"..>+++++++++[<---------->-]<-----.---.+++.---.[-]<<<]"
>||
- 出力

99 Bottles of beer on the wall
99 Bottles of beer
Take one down and pass it around
98 Bottles of beer on the wall

98 Bottles of beer on the wall
98 Bottles of beer
Take one down and pass it around
97 Bottles of beer on the wall

97 Bottles of beer on the wall
97 Bottles of beer
Take one down and pass it around
96 Bottles of beer on the wall

96 Bottles of beer on the wall
96 Bottles of beer
Take one down and pass it around
95 Bottles of beer on the wall

95 Bottles of beer on the wall
95 Bottles of beer
Take one down and pass it around
94 Bottles of beer on the wall

94 Bottles of beer on the wall
94 Bottles of beer
Take one down and pass it around
93 Bottles of beer on the wall

93 Bottles of beer on the wall
93 Bottles of beer
Take one down and pass it around
92 Bottles of beer on the wall

92 Bottles of beer on the wall
92 Bottles of beer
Take one down and pass it around
91 Bottles of beer on the wall

91 Bottles of beer on the wall
91 Bottles of beer
Take one down and pass it around
90 Bottles of beer on the wall

90 Bottles of beer on the wall
90 Bottles of beer
Take one down and pass it around
89 Bottles of beer on the wall

89 Bottles of beer on the wall
89 Bottles of beer
Take one down and pass it around
88 Bottles of beer on the wall

88 Bottles of beer on the wall
88 Bottles of beer
Take one down and pass it around
87 Bottles of beer on the wall

87 Bottles of beer on the wall
87 Bottles of beer
Take one down and pass it around
86 Bottles of beer on the wall

86 Bottles of beer on the wall
86 Bottles of beer
Take one down and pass it around
85 Bottles of beer on the wall

85 Bottles of beer on the wall
85 Bottles of beer
Take one down and pass it around
84 Bottles of beer on the wall

84 Bottles of beer on the wall
84 Bottles of beer
Take one down and pass it around
83 Bottles of beer on the wall

83 Bottles of beer on the wall
83 Bottles of beer
Take one down and pass it around
82 Bottles of beer on the wall

82 Bottles of beer on the wall
82 Bottles of beer
Take one down and pass it around
81 Bottles of beer on the wall

81 Bottles of beer on the wall
81 Bottles of beer
Take one down and pass it around
80 Bottles of beer on the wall

80 Bottles of beer on the wall
80 Bottles of beer
Take one down and pass it around
79 Bottles of beer on the wall

79 Bottles of beer on the wall
79 Bottles of beer
Take one down and pass it around
78 Bottles of beer on the wall

78 Bottles of beer on the wall
78 Bottles of beer
Take one down and pass it around
77 Bottles of beer on the wall

77 Bottles of beer on the wall
77 Bottles of beer
Take one down and pass it around
76 Bottles of beer on the wall

76 Bottles of beer on the wall
76 Bottles of beer
Take one down and pass it around
75 Bottles of beer on the wall

75 Bottles of beer on the wall
75 Bottles of beer
Take one down and pass it around
74 Bottles of beer on the wall

74 Bottles of beer on the wall
74 Bottles of beer
Take one down and pass it around
73 Bottles of beer on the wall

73 Bottles of beer on the wall
73 Bottles of beer
Take one down and pass it around
72 Bottles of beer on the wall

72 Bottles of beer on the wall
72 Bottles of beer
Take one down and pass it around
71 Bottles of beer on the wall

71 Bottles of beer on the wall
71 Bottles of beer
Take one down and pass it around
70 Bottles of beer on the wall

70 Bottles of beer on the wall
70 Bottles of beer
Take one down and pass it around
69 Bottles of beer on the wall

69 Bottles of beer on the wall
69 Bottles of beer
Take one down and pass it around
68 Bottles of beer on the wall

68 Bottles of beer on the wall
68 Bottles of beer
Take one down and pass it around
67 Bottles of beer on the wall

67 Bottles of beer on the wall
67 Bottles of beer
Take one down and pass it around
66 Bottles of beer on the wall

66 Bottles of beer on the wall
66 Bottles of beer
Take one down and pass it around
65 Bottles of beer on the wall

65 Bottles of beer on the wall
65 Bottles of beer
Take one down and pass it around
64 Bottles of beer on the wall

64 Bottles of beer on the wall
64 Bottles of beer
Take one down and pass it around
63 Bottles of beer on the wall

63 Bottles of beer on the wall
63 Bottles of beer
Take one down and pass it around
62 Bottles of beer on the wall

62 Bottles of beer on the wall
62 Bottles of beer
Take one down and pass it around
61 Bottles of beer on the wall

61 Bottles of beer on the wall
61 Bottles of beer
Take one down and pass it around
60 Bottles of beer on the wall

60 Bottles of beer on the wall
60 Bottles of beer
Take one down and pass it around
59 Bottles of beer on the wall

59 Bottles of beer on the wall
59 Bottles of beer
Take one down and pass it around
58 Bottles of beer on the wall

58 Bottles of beer on the wall
58 Bottles of beer
Take one down and pass it around
57 Bottles of beer on the wall

57 Bottles of beer on the wall
57 Bottles of beer
Take one down and pass it around
56 Bottles of beer on the wall

56 Bottles of beer on the wall
56 Bottles of beer
Take one down and pass it around
55 Bottles of beer on the wall

55 Bottles of beer on the wall
55 Bottles of beer
Take one down and pass it around
54 Bottles of beer on the wall

54 Bottles of beer on the wall
54 Bottles of beer
Take one down and pass it around
53 Bottles of beer on the wall

53 Bottles of beer on the wall
53 Bottles of beer
Take one down and pass it around
52 Bottles of beer on the wall

52 Bottles of beer on the wall
52 Bottles of beer
Take one down and pass it around
51 Bottles of beer on the wall

51 Bottles of beer on the wall
51 Bottles of beer
Take one down and pass it around
50 Bottles of beer on the wall

50 Bottles of beer on the wall
50 Bottles of beer
Take one down and pass it around
49 Bottles of beer on the wall

49 Bottles of beer on the wall
49 Bottles of beer
Take one down and pass it around
48 Bottles of beer on the wall

48 Bottles of beer on the wall
48 Bottles of beer
Take one down and pass it around
47 Bottles of beer on the wall

47 Bottles of beer on the wall
47 Bottles of beer
Take one down and pass it around
46 Bottles of beer on the wall

46 Bottles of beer on the wall
46 Bottles of beer
Take one down and pass it around
45 Bottles of beer on the wall

45 Bottles of beer on the wall
45 Bottles of beer
Take one down and pass it around
44 Bottles of beer on the wall

44 Bottles of beer on the wall
44 Bottles of beer
Take one down and pass it around
43 Bottles of beer on the wall

43 Bottles of beer on the wall
43 Bottles of beer
Take one down and pass it around
42 Bottles of beer on the wall

42 Bottles of beer on the wall
42 Bottles of beer
Take one down and pass it around
41 Bottles of beer on the wall

41 Bottles of beer on the wall
41 Bottles of beer
Take one down and pass it around
40 Bottles of beer on the wall

40 Bottles of beer on the wall
40 Bottles of beer
Take one down and pass it around
39 Bottles of beer on the wall

39 Bottles of beer on the wall
39 Bottles of beer
Take one down and pass it around
38 Bottles of beer on the wall

38 Bottles of beer on the wall
38 Bottles of beer
Take one down and pass it around
37 Bottles of beer on the wall

37 Bottles of beer on the wall
37 Bottles of beer
Take one down and pass it around
36 Bottles of beer on the wall

36 Bottles of beer on the wall
36 Bottles of beer
Take one down and pass it around
35 Bottles of beer on the wall

35 Bottles of beer on the wall
35 Bottles of beer
Take one down and pass it around
34 Bottles of beer on the wall

34 Bottles of beer on the wall
34 Bottles of beer
Take one down and pass it around
33 Bottles of beer on the wall

33 Bottles of beer on the wall
33 Bottles of beer
Take one down and pass it around
32 Bottles of beer on the wall

32 Bottles of beer on the wall
32 Bottles of beer
Take one down and pass it around
31 Bottles of beer on the wall

31 Bottles of beer on the wall
31 Bottles of beer
Take one down and pass it around
30 Bottles of beer on the wall

30 Bottles of beer on the wall
30 Bottles of beer
Take one down and pass it around
29 Bottles of beer on the wall

29 Bottles of beer on the wall
29 Bottles of beer
Take one down and pass it around
28 Bottles of beer on the wall

28 Bottles of beer on the wall
28 Bottles of beer
Take one down and pass it around
27 Bottles of beer on the wall

27 Bottles of beer on the wall
27 Bottles of beer
Take one down and pass it around
26 Bottles of beer on the wall

26 Bottles of beer on the wall
26 Bottles of beer
Take one down and pass it around
25 Bottles of beer on the wall

25 Bottles of beer on the wall
25 Bottles of beer
Take one down and pass it around
24 Bottles of beer on the wall

24 Bottles of beer on the wall
24 Bottles of beer
Take one down and pass it around
23 Bottles of beer on the wall

23 Bottles of beer on the wall
23 Bottles of beer
Take one down and pass it around
22 Bottles of beer on the wall

22 Bottles of beer on the wall
22 Bottles of beer
Take one down and pass it around
21 Bottles of beer on the wall

21 Bottles of beer on the wall
21 Bottles of beer
Take one down and pass it around
20 Bottles of beer on the wall

20 Bottles of beer on the wall
20 Bottles of beer
Take one down and pass it around
19 Bottles of beer on the wall

19 Bottles of beer on the wall
19 Bottles of beer
Take one down and pass it around
18 Bottles of beer on the wall

18 Bottles of beer on the wall
18 Bottles of beer
Take one down and pass it around
17 Bottles of beer on the wall

17 Bottles of beer on the wall
17 Bottles of beer
Take one down and pass it around
16 Bottles of beer on the wall

16 Bottles of beer on the wall
16 Bottles of beer
Take one down and pass it around
15 Bottles of beer on the wall

15 Bottles of beer on the wall
15 Bottles of beer
Take one down and pass it around
14 Bottles of beer on the wall

14 Bottles of beer on the wall
14 Bottles of beer
Take one down and pass it around
13 Bottles of beer on the wall

13 Bottles of beer on the wall
13 Bottles of beer
Take one down and pass it around
12 Bottles of beer on the wall

12 Bottles of beer on the wall
12 Bottles of beer
Take one down and pass it around
11 Bottles of beer on the wall

11 Bottles of beer on the wall
11 Bottles of beer
Take one down and pass it around
10 Bottles of beer on the wall

10 Bottles of beer on the wall
10 Bottles of beer
Take one down and pass it around
9 Bottles of beer on the wall

9 Bottles of beer on the wall
9 Bottles of beer
Take one down and pass it around
8 Bottles of beer on the wall

8 Bottles of beer on the wall
8 Bottles of beer
Take one down and pass it around
7 Bottles of beer on the wall

7 Bottles of beer on the wall
7 Bottles of beer
Take one down and pass it around
6 Bottles of beer on the wall

6 Bottles of beer on the wall
6 Bottles of beer
Take one down and pass it around
5 Bottles of beer on the wall

5 Bottles of beer on the wall
5 Bottles of beer
Take one down and pass it around
4 Bottles of beer on the wall

4 Bottles of beer on the wall
4 Bottles of beer
Take one down and pass it around
3 Bottles of beer on the wall

3 Bottles of beer on the wall
3 Bottles of beer
Take one down and pass it around
2 Bottles of beer on the wall

2 Bottles of beer on the wall
2 Bottles of beer
Take one down and pass it around
1 Bottle of beer on the wall

1 Bottle of beer on the wall
1 Bottle of beer
Take one down and pass it around
0 Bottles of beer on the wall
|



  • self.bf (Brainfuck Golf 第0回コンテストの課題: 入力されたBrainfuckコードと「同じ文字列を出力するBrainfuckコード」を出力するプログラム)
// Self-describing
"+++++[>+++++++++<-],[[>--.++>+<<-]>+.->[<.>-]<<,]"
  • 入力
+++++[>+++++++++<-],[[>--.++>+<<-]>+.->[<.>-]<<,]
  • 出力
+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++.----------



素晴らしい。

まとめ

  • constexpr として実装されたコードは、単にコンパイル時に計算できるというだけでなく、モダンな設計がなされているという指標にもなる。
  • constexpr は進化する。まだまだもっとコンパイル時処理をエンジョイ&エキサイティングしよう。


24日目の記事は usagi 氏です。
よろしくお願いします。

暗黒定数式 Vol.1 の印刷事情

同人おじさんです。


今回は印刷所のステマをしようと思う。


文学フリマで頒布した『暗黒定数式 Vol.1』は420ページあり、これを印刷所に発注しようとするとかなり選択肢が狭まってしまう。というのも、ほとんどの同人誌印刷所のセット料金表は、小説本向けのセットでも300ページまでしか記載がない。
別に見積もりを取ったり、設定された追加料金を加えれば(許容範囲内であれば)何ページであろうと発注できるが、必然的に高価格になる。何千部も刷るならともかく、100部程度だと単価が異常に上がってしまう。
なのでそのあたりを念頭に検討をした。その過程で知りえたいくつかの印刷所の特長を備忘録的に記しておく。


ちなみに、暗黒定数式において前提としたのは以下のような項目だ。

  • 文庫や新書サイズだとページ数が嵩みすぎるのでA5判にする。
  • カラー絵2ページ×2箇所。
  • 別刷りのカバーか、もしくは少なくとも表紙にPP加工はかける。


ベテランの同人おじさんにはほとんど今更のことだろうが、同人印刷にさほど詳しくないといった文章系の同人おじさんには参考になるかもしれない。

ポプルス

http://www.inv.co.jp/~popls/

オンデマンド印刷の老舗。文学フリマ参加サークルではシェアが結構あるらしい。
「ソフト書籍セット」というのがあり、カバーと帯のほか、表2,3に見返しが貼られるという珍しい仕様だ。
カバーがラシャ紙なのも珍しいが、実際どういう感じになるかはわからない。コート紙+PPのカバーにしたい場合はオプション料金が必要だ。
「カバー付き文庫・新書セット」というのもあるが、これはA5判はできない。残念だ。


特筆すべきは「いろもじオプション」というオプションだ。
モノクロの版面にカラーを追加できるのだが、版面の1/3以内であればフルカラー画像も配置できる。
他のほとんどの印刷所では、カラーページを追加しようとするときは「カラー口絵」扱いとなり、紙も変わる。何ページもやろうとするとオプション料金も膨れあがり、フルカラーのセットと変わりなくなる。
いろもじオプションであれば、制限はあるが、数十ページに適用しても安くつく。
カラー図表がちょこちょこと入った論文集のようなものを刷る場合は、これ一択ではないかと思う。


オンデマンド印刷の質についてだが、取り寄せたサンプルを見る限りではフルカラー印刷の色はかなりよく出ていると思う。
ただし、PP加工をかけた場合はオンデマンド独特のトナーの盛りがかなり目立つ。マットPPであればそれほどでもない。

ちょ古っ都製本工房

http://www.chokotto.jp/

オンデマンド印刷で、会社自体は老舗だが、同人印刷に本格参入したのはそう昔ではないようだ。
シンプルな装丁に限れば最安クラスだろう。一番安いセットはとにかく安い。
口絵を入れるなど仕様上の都合もあって今回は見送ったが、小冊子でもつくる機会があればぜひ使いたい。
安かろう~という評判も聞くが、文学フリマで購入した知人の本を見る限りではそれなりの出来だ。


書籍用紙(淡クリームキンマリ)の連量72.5kgが標準で選択できるのも嬉しい。


余談だが、ここは元々は学校向け教材や企業向けマニュアルを主に印刷していたが、一般のお客様(と書いてあるがつまり同人界隈の人間)からの要望が増加したため、同人誌をメインターゲットにした部門を立ち上げたという経緯のようである。
上記は当社のサイトに書いてあることで、ここからは2chの同人スレを読んだ上での想像なので信憑性はないが、同人スレに格安の印刷所として当社(当時はまだ同人誌メインではなかった)の情報が貼られ、それを目にしたスレ住人が問い合わせまくったのが発端らしい。
営業的には嬉しい悲鳴でもあっただろうが、印刷製本のクオリティにやたらと厳しい同人界隈の人間を相手にするのは、さぞ苦労であったろうと想像する。

ねこのしっぽ

http://www.shippo.co.jp/neko/

オフセット印刷ではかなり安い方だと思われる。
「文庫と新書のフルセット」や「ペーパーバック本」などのセットがあるが、いずれも文庫/新書サイズ専用。
問い合わせたところ、A5でそれらの装丁に近いものを発注するには、「コミックスセット」のほうでの対応になるそうだ。


ちなみにカバー巻きはやっていないので、巻く作業は自分でやるのが前提になる。(折りやすいようスジ入れ加工は付いている)
カバー巻き加工のオプションもあるにはあるが、専門業者への委託となるため納期が3週間と大幅に遅くなる。
また、オンライン入稿かつセット仕様外の発注の場合は事前に受付に問い合わせが必要になる。
価格は申し分ないのだが、痒いところに手の届かない感が所々ある。


本以外では、オンデマンドのCDフロントジャケットの印刷が安い。
プレスやCDジャケット印刷を専門にしている業者や、同業他社の印刷所を含めても、格安の部類ではないだろうか。
また、オンラインストアではB1ポスターを展示可能なポスタースタンドなど、ありそうでなかなか見つからない商品があったりする。
実は書籍印刷以外の方面のほうが強かったりするのではないだろうか。

栄光

https://www.eikou.com/

これもオフセット印刷ではかなり安い。
「ノベルセット」というのがあり、文庫~A5判まで選択可能だ。
300ページを越える場合は見積もりになるが、420ページかつオフセット印刷の仕様では検討した中で一番安かった。
計算式は不明だが、ページ数増加に対する追加料金の係数が小さいのだと思われる。
(ほどんどの印刷所では、例えば300ページ以降は+8ページ毎にn円、といった計算になる)


それと、元同級生であるBL漫画描きからの評価も良かったため、今回は当社に発注することにした。


ちなみに、執筆と編集の都合上自分の判断で〆切を遅らせたのが理由で、〆切の早いノベルセットは使えなかったため、「サンバカーニバル」というセットで発注した。
〆切が全体的に同業他社より遅いのもここの特長であるようだ。
なお増刷分はノベルセットで発注してある。

プリントパック

http://www.printpac.co.jp/

ここは同人誌印刷所ではないのだが、仕事の関係では専らここを使っている。
同人誌の表紙印刷も扱っているが、表紙のみの印刷であれば関西美術印刷のほうが有名だろう。
ポスター、チラシ、名刺、ポストカードといったものの印刷が安く納期も早い。


オンデマンド印刷ポスターを発注しようと思ったのだが、残念ながら時間的余裕がなかったため、今回は自宅のレーザープリンターで刷ってラミネート加工をかけ貼り合わせることにした。

印刷全般について

印刷を考えるにあたって最初の大きな選択肢は、オフセット印刷かオンデマンド印刷かという点だろう。
百も承知という人間は読み飛ばされたし。


オフセット印刷の版を制作して印刷する。ゆえにイニシャルコストがかかるが、多く刷るほどスケールメリットが出る。
対してオンデマンド印刷はプリンターと同様のデジタル印刷機で印刷する。イニシャルコストは低いがスケールメリットはない。
以前見たグラフによれば、平均的な同人誌で300部がオフセット印刷のスケールメリットがオンデマンド印刷を追い越す(つまり単価が安くなる)分岐点だそうだ。
もちろん印刷物の仕様によるのだろうが、『暗黒定数式 Vol.1』について今回検討した限りでは、100部で同等かオンデマンドが若干安い程度、200部ですでに逆転するといった印象だ。


オンデマンド印刷のデメリットとしてよく挙げられるのは、以下のような点だ。

  • PP加工をかけた時にベタ部分の境界にトナーの段差が見える
  • PP加工が剥がれやすい(特に縁がベタになる場合)
  • 黒ベタがテカって見える
  • 文字が実際のウェイトより太りやすい
  • 淡い色で色ムラが出やすい
  • 湿気のある環境で製本後の本に波打ちが発生しやすい


実際にサンプルを見た印象としても、同じような感想を持った。
とはいえ、フルカラー印刷の色の表現についてはオフセット印刷と較べてもほとんど遜色がなく、そういう意識で見たり光を当てたり斜めから見たりしない限りは気づかないレベルだろう。


今回は、『暗黒定数式 Vol.1』の仕様と部数ではオンデマンドとオフセットで価格が大して変わりなかったため、オフセット印刷を選択した。

その他

上記は小説本である『暗黒定数式 Vol.1』を念頭に検討したものだが、漫画やイラスト本の場合はまた色々違った観点があるだろう。


文章主体の本に限って言えば、ワガママを言いたい部分がいくつかある。
ワガママであって文句ではないことは申しておく。

  • 版面一部にカラー図表を入れたい場合は、その都度口絵オプションにするか、フルカラー本にするかしかほとんど選択肢がない。いずれも高額になる。ポプルスのいろもじオプションのような仕様が同業他社にも広がってほしい。
  • 星海社文庫のような、本文の書籍用紙に口絵もそのまま印刷するような仕様がほとんどの同人誌印刷ではできない。カラー口絵のオプションではほとんどがコート紙で、書籍用紙にはカラー印刷できない。星海社自体は好きではないのだが装丁は好きなので出来るようにならないものか。
  • 300ページを大幅に超えるような印刷がページ数比にして高額になる。丁合や製本の手間や技術がいることは想像つくのだが、何とかならないものか。鈍器本やアコーディオン本がつくれない。
  • 淡クリームの書籍用紙(文庫本で一般的に使われる)がセットに標準の紙となる傾向が業界の動向としてあるようで、これは非常に嬉しい。しかし標準に含まれるのは連量90.0kgの書籍用紙で、より薄い連量72.5kgなどはオプション扱いであることがほとんどだ。薄いほうの書籍用紙も標準で使えるようになればなお嬉しいのだが。
  • これは単なる疑問なのだが、表紙の両端を長くしてカバーのような折り返しにする装丁を、同人誌印刷業界ではどういうわけか「フランス表紙」「フランス装」などと称するようだ。フランス装は本来、表紙の天地小口の三方を内側に折り返した、上製本の糊付けをする前のような状態にした装丁のことを言うはずだが、同人業界では奇妙な変化をしたらしい。


以上、いろいろ思うところを書いた。
さて、暗黒定数式 Vol.2 の装丁はどうするか。
いろいろ構想するのも楽しいが、まず本編を完成させないことにはどうにもならないし、それ以前に本業で進捗しないと動きようもない。
進捗せざるもの生きるべからず。

第十九回文学フリマに参加してきました 『暗黒定数式 The Dark constexpr』

 中3女子です。

 もう一週間くらい経ちましたが、11月24日に東京流通センターで開催された第十九回文学フリマに参加してきました。
 サークルは『暗黒定数式 The Dark constexpr』、SF合同誌『暗黒定数式 Vol.1』を頒布してきました。


 もともとぼくのツイッターの TL には理系や情報系の人が多いんですが、絵とか音楽とか小説とか創作の畑に手を出している人もわりと居るんですね。
 かくいうぼくも小説を書くので、フォロワーに呼びかけたところ、みごと執筆陣が揃ったので合同誌を出すことになりました。
 情報系は変態ばかりだとぼくの TL では専らの評判ですが、変態の書く小説はさぞ面白いだろうな、と。
 あ、最初に誘った南正太郎氏は情報系じゃないんですが、理学部出の変態なので同じことです。


 当日スペースに掲示した POP がこれ↓です。
f:id:boleros:20141203145335j:plain
 内容がなんとなく察せられますね!


 A5判420ページの大ボリュームなので読み応えだけは保証できます。
 100部刷ってうち70部を会場に送ったんですが、30部くらいはけたら御の字かな~と考えていたらなんと完売しました。
 自宅の在庫のほうも、取り置きや寄稿者の配分・購入分を発送したらほとんど無くなってしまいました。
 いや、驚きでしたね。
 せっかく後日「在庫70部同人爆死おじさん」とかツイートする心の準備をしてたんですが。
 なので調子に乗って増刷して、委託や通販もすることにしました。これで残ったら心置きなく、在庫を抱えて爆死同人おじさんネタができますね。
 まあもともと原価割れしていて、どのみち最初から爆死しているのでもう何も怖くないんですが……。


 今回文フリ初参加だったのですが、さすがに「文学」オンリーなだけあって他の同人イベントとはひと味違う雰囲気でしたね。
 コミケとかだと字書き主流のジャンルでもないかぎり「あ、小説なんですか……(そっと本を戻す)」といった感じなのが字書きの扱いですからね。
 文フリは何というか字書きが「承認されてる~~」という感じでした。承認欲求おじさんにとっては天国っぽいです。


 会場でフォロワーの方に差し入れを戴いたりしたのも嬉しかったです。
 買ってくれた人もフォロワーの方が多い感じで、すごくありがたいです。
 こういうのは基本的にクソ馴れ合いだと思っているんですが、いざ自分が恩恵を受ける立場になると身をゆだねてしまいますね。
 気持ちいい。これは麻薬だ。
 その一方、前評判が膨らんでる感じでむしろ読んだ後の反応が怖いですね。


 周りのサークルも巡ってきたんですが、いや、すごいですね。尖った自意識がビシビシきます。
 気づいたら40冊くらい購入してました。
 積ん読がタワーみたいな状態になったので少しずつ消化してるところです。
 まだ数冊しか読んでないんですが、梟流の『Liminality』と大文妄の妄点vol.7に収録の『フィリオトゥーラ』はなかなかぐっとくる小説でしたね。
 気が向いたら書評(というか感想)記事も書いていきたいです。


 ちなみに、『暗黒定数式 Vol.1』の増刷分は、コミケC87 2日目(2014/12/29) 東6 ソ-08a 『RedTailCat』 様に委託いたします。
 暗黒定数式メンバーである as_capabl 氏の参加するサークルなんですが、ご厚意で置いて戴けることになりました。
 『RedTailCat』は東方小説を出しているサークルですので、そちらのほうもぜひ。


 来年はぼくの予定がちょっとギチギチに入ってるのでどうなるかわかりませんが、できれば次回『暗黒定数式 Vol.2』を出したいところです。
 通販だとかの情報は 暗黒定数式 The Dark constexpr のほうで告知しますので、よろしくお願いいたします。

機械になりたいという願い

 今回は書評のようなことをします。
 ツイッターで知り合った友人が小説を書いているのですが、その中で主人公がこうした独白をします。

 ――機械になりたい。
 かつて希海はそう思っていた。今でもそう思っている。
 機械なら臆病風や怠惰といった心に負けることもなく、自分がやろうと思ったことができる。機械なら嫌われることや失敗することを恐れたりなんかはしない。

ふたりのハードプロブレム


 機械になりたい、というのはいったいどういう種類の願望なんでしょうか。
 たとえばこれを不死願望や変身願望の一種のようなものと捉えるならば、人間にとって普遍的な願望のひとつであるとも言えるでしょう。

 吸血鬼のような不老不死の存在は古今東西さまざまな創作においてたびたびモチーフとされてきました。
 機械的存在に限定しても、生体部品を機械に置換して肉体をサイボーグ化したり、意識を情報化して電脳空間上の実体として生きたりといった手段で得る半永久的生存は、とくに SF における古典的テーマといえます。

 いっぽう、『銀河鉄道999』において主人公鉄郎が「機械の身体を手に入れたい」と願ったのは、機械化人に復讐を果たす力を手にするという目的のためでした。鉄郎自身は永遠に生きたいなどと思ってはいないわけですから、これは「人間以上の力を得たい」という変身願望に分類すべきでしょう。


 では、この作品『ふたりのハードプロブレム』における主人公青山希海についてはどうでしょうか。
 青山希海は大学に不登校中の引きこもり(女子)です。
 自身そうした境遇を快く思っているはずもなく、内罰的な思いを抱えています。
 そのような心の弱さを持った存在から、そんなものを持たない存在へ転換したいという逃避的な感情からくる変身願望であると、表側からはそう読むことができます。
 しかしそれが全てだと言いきることはできません。


 作品のヒロインであるアンドロイド・リコはきわめて高度なニューラルプロセッサで動作する非常に人間に近しい人格をもっています。
 とうぜん多様な感情や学習能力をそなえており、実装された感情のなかにはおそれや悩みといったネガティブな方面のものも含まれていることでしょう。
 どれだけ科学技術が進歩すればそこまで至れるかはともかくとして、人工知能研究といった現代の科学の歩みが「より人間らしい機械」という方向に向いていることは間違いないでしょう。


 しかし、青山希海はそれを拒絶します。

「何が人間よ」
 結局はそこなのかも知れない。東の言っていることは一から十まで理解できる。納得がいかないのは、その前提だ。アンドロイドをどこまでも人間に近づけるというその思想が、希海にはどうしても受け入れられなかった。
「そんなに人間が作りたきゃ……さっさと結婚でもすればいいのよ」

ふたりのハードプロブレム

 青山希海にとって、不全な感情を排した機械とは彼女自身がそうありたいと望む理想であって、それをわざわざ人間としての自分のように不全な存在へと近づけるというのは、いわば理想に対する侮辱と同義であったということでしょう。


 とはいえ、十分に進歩した機械が肉体のうえでも意識のうえでも人間同等の機能を有しうるという事実は、希海自身も認めざるを得ないでしょう。

 遠く離れた宇宙の核融合炉と、そこから発せられたおびただしい数の光子フォトン。その一部が1億5000万kmという途方もない距離を旅し地球にやってきた。大部分は大気に散乱されるも、いくらかの光子は希海の目にまで到達する。瞳孔をくぐり抜けた光は水晶体に分極を発生させながらその進路を曲げ、網膜に像を結ぶ。像を結んだ光子は光感受性蛋白質ロドプシンやフォトプシンの電子を励起状態へと蹴り上げ、パルス状の電気信号が爆竹のように視神経を伝わっていく。

ふたりのハードプロブレム

 なぜなら、冒頭の描写からも分かるように、人間のふるまいもまた機械的原理のメカニズムにもとづく複雑な相互作用の総体がその実体であるということを彼女は理解しています。
 そうした人間機械論的立場に立つならば、人間性と非人間性とを分かつものは人や機械といった素材や繁殖手段などの定義分類によるものではなく、そこに実装されたふるまいの違いにすぎないことも演繹できます。
 してみると、「機械になりたい」という願いは、青山希海を機械論的対象として捉えるならば「ロボットが自分に実装された機能を廃止したい」と言っているのと本質的に変わらず、「機械」というテーゼが事実上意味をなさないものになってしまいます。
 では青山希海の考える「人間と機械」にはそれ以上のどういった差異があるのでしょう。
 そこに「機械になりたい」という願いの裏側の本質があるとぼくは(勝手に)思っています。


 われわれ人間と機械が決定的に異なるのは、機械は設計された解析可能な存在であるということです。
 機械がどれだけ複雑で不可解なふるまいをしようとも、たとえそれが人間の意識以上に高度な作用が生じていたとしても、設計し実装した人間はそれが「どのように作られたか」という原理を理解し、解析することができます。
 現代の機械はハード、ソフトともに非常に密であって、ひとりの技術者が把握しきれないものではあるけれど、だれかの理論、だれかの設計、だれかの実装、そうした複雑だけれどトレーサビリティのある技術の総体にほかなりません。機械は、ふるまいの結果からではなく設計レベルからの解析をすることが可能です。


 ところが、人間はそうはいきません。
 人間が機械論的に決定可能な存在だとしても、いまのところ人類にとって人間自身は巨大なブラックボックスの塊です。
 脳神経の活動から、感情が脳のどこの部位の活動によって生じているかを観測することはできても、なぜ、どのように神経がそのように配置されたかという設計者の意図を解析することはできません。
(これに対してもっとも非科学的に解決を与えようとすれば神の存在を持ち出せば済みます)


 アンドロイドであるリコが持つ感情が、なぜそうであるかと問えば「そのように設計・学習された」からだと答えることができます。必要とあらば、その基礎原理やハードウェアの設計書やソフトウェアのソースコードをひとつひとつ並べることも可能でしょう。
 けれど人間である青山希海は、怠惰や失敗へのおそれといった感情を、「人間であるから」という以上に解析することはできません。解析できずに甘受するしかないからこそ苦しむのです。


 このような観点に立つならば、「機械になりたい」という願いは、「解析可能な存在になりたい」という願いに言い換えることができます。
 それは、自分自身がほかならぬ連続的な実体としての自分という存在を認識することでもあります。つまり「完全な」アイデンティティの確立です。
 なぜ自分はこのような自分なのか。
 ある意味では、それを理解すること以上に確固たる自己を持った人間として生きるための精神的支柱はありません。

 自分自身の依って立つ原理を理解する――それは SF でいえばグレッグ・イーガンの『しあわせの理由』『決断者』『祈りの海』などで形を変えながら述べられてきたテーマでもあります。


 ぼく自身は「機械になりたい」と願ったことはありません。
 なぜといえば、人間というハードウェアはほかのあらゆる機械よりも汎用で冗長性がありすぐれた機能をもったハードであると認識しているからです。
 けれども、そのハードウェアに致命的な不具合を感じ、不具合を解析不能なハードに不満を抱いたとしたら……機械になりたい、それが一つの魅力的な選択肢として演繹されるであろうことは否定できません。


 まあ、『ふたりのハードプロブレム』という物語自体は、そうした存在論的テーマをちりばめつつも、女の子どうしがくんずほぐれつしたり一つマフラーでデートしたりあんなことやそんなことになったりします。
 もっとお願いします。

 なおこの作品はまだ完結していません。
 なのでどのような結論、結末が用意されているかはまったく分かりません。
 青春を床に落としたような主人公希海がリコの存在によって救われていくのは間違いないようですが。


 ちなみに、『ふたりのハードプロブレム』はぼくが今冬の第十九回文学フリマで出す予定の合同誌 暗黒定数式 The Dark constexpr に収録される予定です。
 このステマに興味を持たれた方はぜひお待ちしております。