ボレロ村上 - ENiyGmaA Code

中3女子です。

コンパイル時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 氏です。
よろしくお願いします。