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++ -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++なのでラップして型安全度を上げた、ということにしておきます。