HaskellのIntegerをC++で使う

C++の標準には多倍長整数ライブラリがありません。これは大変なことだと思います。
まあBoost.Multiprecision来るじゃないというのはさておき、
標準の多倍長整数がないなら他言語の標準から持ってくればいいということで、HaskellのIntegerをラップしてC++多倍長整数クラスにしてみました。
まずIntegerを扱う関数をFFIでエクスポートします。Integerを引数・戻り値にする関数はエクスポートできないので、代わりにStablePtr Integerを使います。これはIntegerオブジェクトを指すポインタで、GCに回収・再配置されない保証があります。CやC++からはただのvoid *に見えます。
とりあえずfromIntegral、(*)、showをエクスポートしました(今回は乗法と出力がしたいので)。StablePtr IntegerとCStringの解放関数も一緒にエクスポートしておきます。
HsInteger.hs

module HsInteger where

import Foreign
import Foreign.C.String
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Control.Monad

type CInteger = StablePtr Integer

foreign export ccall hs_integer_from_int :: CInt -> IO CInteger
hs_integer_from_int (CInt n) = newStablePtr $! fromIntegral n

foreign export ccall "hs_integer_free" freeStablePtr :: CInteger -> IO ()

foreign export ccall hs_integer_multiply :: CInteger -> CInteger -> IO CInteger
hs_integer_multiply m n = liftM2 (*) (deRefStablePtr m) (deRefStablePtr n) >>= (newStablePtr $!)

foreign export ccall hs_integer_to_string :: CInteger -> IO CString
hs_integer_to_string = deRefStablePtr >=> newCString . show

foreign export ccall "hs_integer_free_string" free :: CString -> IO ()

コンパイルしてHsInteger_stub.hとHsInteger_stub.oを作ります。

$ ghc -c HsInteger.hs

C++側ではこれらの関数をラップしてhs_integerクラスに収めます。
hs_integer.hpp

#ifndef HS_INTEGER_HPP
#define HS_INTEGER_HPP

#include "HsInteger_stub.h"
#include <memory>
#include <ostream>
#include <boost/operators.hpp>

class hs_integer
  : public boost::multipliable<hs_integer>
{
public:
    hs_integer(int n = 0)
      : ptr(hs_integer_from_int(n), &hs_integer_free)
    {}

    hs_integer &operator*=(hs_integer const &rhs)
    {
        ptr.reset(hs_integer_multiply(ptr.get(), rhs.ptr.get()), &hs_integer_free);
        return *this;
    }

    template <class Char, class CharTraits>
    friend std::basic_ostream<Char, CharTraits> &
    operator<<(std::basic_ostream<Char, CharTraits> &os, hs_integer const &n)
    {
        std::unique_ptr<void, void (*)(void *)> const
        str(hs_integer_to_string(n.ptr.get()), &hs_integer_free_string);
        return os << static_cast<char *>(str.get());
    }

private:
    std::shared_ptr<void> ptr;
};

#endif

では使ってみましょう。100!を計算し出力するプログラムです。GHCランタイムシステムの初期化と終了を忘れないように。
hs_integer_example.cpp

#include <iostream>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/numeric.hpp>
#include <boost/scope_exit.hpp>
#include "hs_integer.hpp"

int main(int argc, char *argv[])
{
    // initialize and terminate GHC's runtime system
    hs_init(&argc, &argv);
    BOOST_SCOPE_EXIT(void) { hs_exit(); } BOOST_SCOPE_EXIT_END

    using namespace boost::phoenix::placeholders;
    std::cout << "100!=" <<
        // hs_integer(1) * 1 * 2 * ... * 100
        boost::accumulate(boost::irange(1, 101), hs_integer(1), _1 * _2)
    << std::endl;
}

コンパイルにはg++を、リンクはghcを使うのが楽です。

$ g++ -c -I/path/to/haskell-platform/lib/include hs_integer_example.cpp
$ ghc -no-hs-main HsInteger_stub.o hs_integer_example.o -lstdc++ -o hs_integer_example
$ ./hs_integer_example
100!=9332621544...

うまく動いています。
こんな感じで完全な多倍長整数クラスを作れると思います。なおネタなのでパフォーマンスとかは知りません。

おまけ

ついでにPythonでもやってみました。
py_integer.hpp

#ifndef PY_INTEGER_HPP
#define PY_INTEGER_HPP

#include <ostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/operators.hpp>
#include <boost/python.hpp>

class py_integer
  : public boost::multipliable<py_integer>
{
public:
    py_integer(int n = 0)
      : obj(n)
    {}

    py_integer &operator*=(py_integer const &rhs)
    {
        obj *= rhs.obj;
        return *this;
    }

    template <class Char, class CharTraits>
    friend std::basic_ostream<Char, CharTraits> &
    operator<<(std::basic_ostream<Char, CharTraits> &os, py_integer const &n)
    {
        return os << boost::python::extract<char const *>(boost::python::str(n.obj));
    }

private:
    boost::python::object obj;
};

#endif

py_integer_example.cpp

#include <iostream>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/numeric.hpp>
#include "py_integer.hpp"

int main()
{
    // initialize Python
    Py_Initialize();

    using boost::phoenix::placeholders::_1;
    using boost::phoenix::placeholders::_2;
    std::cout << "100!=" <<
        // py_integer(1) * 1 * 2 * ... * 100
        boost::accumulate(boost::irange(1, 101), py_integer(1), _1 * _2)
    << std::endl;
}
$ g++ py_integer_example.cpp -lpython27 -lboost_python -o py_integer_example
$ ./py_integer_example
100!=9332621544...

boost::python::objectが優秀なので、これを軽く包むだけで実装できてしまいます。operator*=がオーバーロードされているあたりがすごい。
ここまで優秀だとboost::python::objectを直接使えばいい感じですが、せっかくC++なのでラップして型安全度を上げた、ということにしておきます。