Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++で作るWebアプリケーション

 C++で作るWebアプリケーション

C++をJavaScriptにコンパイルするコンパイラEmscriptenを使って、C++でWebアプリケーションを開発する手順を解説します

Fadis

May 09, 2015
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. WEB

  2. JavaScript͸ 8&#ϖʔδʹ؆୯ͳಈ͖Λ ༩͑ΔͨΊʹߟ͑ͩ͞Εͨ ੈلͷ຤ <html><head></head>! <script language="javascript"><!--! var step =

    0;! function update() {! step++;! var block = document.getElementById( "marquee" );! block.style.left = step % 200;! }! --></script>! <body onload="window.setInterval( update, 100 );" >! <div id="marquee" style="position:absolute;width:100px;" >! Hello, World!! </div>! </body>! </html>
  3. C++Ͱ࡞Δ WEBΞϓϦέʔγϣϯ #include <iostream > int m ain() { std::cout

    << “Hello, W orld!” << std::endl; } Naomasa Matsubayashi
  4. LLVM ࠷దԽ clang llvm- pytho lawk ghc perl6 flang ldc

    x86 Power PC ARM MIPS SPARC JVM DCPU- 16
  5. $ ../emscripten/em++ hello.cpp $ ls! a.out.js hello.cpp $ node a.out.js!

    Hello, World! #include <iostream>! ! int main() {! std::cout << “Hello, World!” << std::endl;! }
  6. function _main() {! var label = 0;! var $this_addr_i;! var

    $__pf_addr_i;! var $call=__ZNSt3__1lsINS_11char_traitsIcEEEERNS_1 3basic_ostreamIcT_EES6_PKc(13760, ((2096)|0));! $this_addr_i=$call;! $__pf_addr_i=12;! var $this1_i=$this_addr_i;! var $0=$__pf_addr_i;! var $call_i=FUNCTION_TABLE[$0]($this1_i);! return 0;! }! Module["_main"] = _main; std::__1::basic_ostream<! char,! std::__1::char_traits<char>! >& std::__1::operator<< <! std::__1::char_traits<char>! >(! std::__1::basic_ostream<! char,! std::__1::char_traits<char>! >&,! char const*! )
  7. int main() {! while( 1 ) {! // ͳΜ͔ॲཧ! }!

    } ϝΠϯϧʔϓ μϝɺθολΠ
  8. #include <emscripten.h>! int main_loop {! // ͳΜ͔ॲཧ! }! int main()

    {! emscripten_set_main_loop( &main_loop, 60, 0 );! } JavaScriptͷTFU*OUFSWBMͰNBJO@MPPQΛఆظతʹݺͿ emscripten_set_main_loop
  9. खͰॻ͍ͨJavaScript͔Β Emscripten͕ੜ੒ͨؔ͠਺Λݺͼग़͢ C++ JavaScript extern “C”! int hoge( int a

    ) {! /* ͳΜ͔ॲཧ */! return 5;! } var b =! Module._hoge( 3 ); γϯϘϧ໊
  10. खͰॻ͍ͨJavaScript͔Β Emscripten͕ੜ੒ͨؔ͠਺Λݺͼग़͢ C++Ͱॻ͔Εͨߴ଎ϑʔϦΤม׵ΛJavaScript͔Β࢖͏ྫ #include <array>! #include <utility>! #include <boost/range/iterator_range.hpp>! #include

    <boost/range/algorithm/copy.hpp>! #include <boost/range/algorithm/transform.hpp>! #include "kissfft.hh"! constexpr size_t fft_size = 2048u; ! extern "C" void fft( float *in, float *out ) {! typedef kissfft< float >::cpx_type cpx;! std::array< cpx, fft_size > in_complex;! boost::copy(! boost::iterator_range< float* >( in, in + fft_size ),! boost::begin( in_complex )! );! std::array< cpx, fft_size > out_complex;! kissfft< float > fft_( fft_size, false );! fft_.transform( in_complex.data(), out_complex.data() );! boost::transform( out_complex, out, []( const cpx &e ) {! return e.real();! } );! }
  11. खͰॻ͍ͨJavaScript͔Β Emscripten͕ੜ੒ͨؔ͠਺Λݺͼग़͢ C++Ͱॻ͔Εͨߴ଎ϑʔϦΤม׵ΛJavaScript͔Β࢖͏ྫ $ ../emscripten/em++ -std=c++0x! -I../boost/include fft.cpp -o fft.js

    -s EXPORTED_FUNCTIONS="['_fft']"! -s ASM_JS=1 -O2 CPPTUͷϔομ࢖͏ΜͰΑΖ͘͠ +BWB4DSJQUϑΝΠϧܗࣜͰग़ྗͯ͠Ͷ γϯϘϧ@⒎U͸֎෦͔ΒࢀরՄೳͰͳ͚Ε͹ͳΒͳ͍ BTNKT࢖ͬͯͶ
  12. खͰॻ͍ͨJavaScript͔Β Emscripten͕ੜ੒ͨؔ͠਺Λݺͼग़͢ C++Ͱॻ͔Εͨߴ଎ϑʔϦΤม׵ΛJavaScript͔Β࢖͏ྫ ...! <script language="javascript" src="./fft.js"></script>! <script language="javascript"><!--! function

    run() {! var before = document.getElementById('before');! var after = document.getElementById('after');! var input = Module._malloc( 2048 * 4 );! var output = Module._malloc( 2048 * 4 );! var i;! for( i = 0; i != 2048; i++ ) {! setValue( input + i * 4, Math.random(), 'float' );! before.innerText += Math.floor( getValue( input + i * 4, 'float' ) * 1000 ) / 1000 + ',';! if( i % 16 == 15 ) {! before.innerText += '\n';! }! }! Module._fft( input, output );! for( i = 0; i != 2048; i++ ) {! ϝϞϦྖҬΛ֬อ C++ଆͷؔ਺Λݺͼग़͢ JOQVUʹཚ਺Λઃఆͯ͠ ͍ͭͰʹදࣔ TFU7BMVF ΞυϨε ஋ --7.Ͱͷܕ  ࢦఆͨ͠ΞυϨεʹɺ஋Ληοτ͢Δɻ ܕ͸ୈࡾҾ਺Ͱࢦఆͨ͠ܕͩͬͨ͜ͱʹ͢Δɻ HFU7BMVF ΞυϨε --7.Ͱͷܕ  ࢦఆͨ͠ΞυϨε͔Β஋ΛಡΈग़͢ɻ ܕ͸ୈࡾҾ਺Ͱࢦఆͨ͠ܕͩͬͨ͜ͱʹ͢Δɻ
  13. खͰॻ͍ͨJavaScript͔Β Emscripten͕ੜ੒ͨؔ͠਺Λݺͼग़͢ C++Ͱॻ͔Εͨߴ଎ϑʔϦΤม׵ΛJavaScript͔Β࢖͏ྫ for( i = 0; i != 2048;

    i++ ) {! setValue( input + i * 4, Math.random(), 'float' );! before.innerText += Math.floor( getValue( input + i * 4, 'float' ) * 1000 ) / 1000 + ',';! if( i % 16 == 15 ) {! before.innerText += '\n';! }! }! Module._fft( input, output );! for( i = 0; i != 2048; i++ ) {! after.innerText += Math.floor( getValue( output + i * 4, 'float' ) * 1000 ) / 1000 + ',';! if( i % 16 == 15 ) {! after.innerText += '\n';! }! }! Module._free( input );! Module._free( output );! }! --></script>! </head>! <body onload="run()" >! <div class="console" id="before"></div>! <div class="console" id="after"></div>! </body>! </html> PVUQVUͷ಺༰Λग़ྗ ϝϞϦྖҬΛഁغ
  14. #include <emscripten/bind.h> #include <boost/functional/hash.hpp> size_t calc_hash( const std::string &src )

    { return boost::hash< std::string >()( src ); } using namespace emscripten; EMSCRIPTEN_BINDINGS(mod) { function("hash", &calc_hash); } ͜ΕΛ ͜ͷ໊લͰ ؔ਺Λࢦఆ໊ͨ͠લͰJavaScriptଆʹݟͤΔ Module.hash( value );
  15. C++ͷߏ଄ମʹJavaScript͔ΒΞΫηε͢Δ #include <emscripten/bind.h>! #include <utility>! using namespace emscripten;! EMSCRIPTEN_BINDINGS(mod) {!

    value_struct< std::pair<float,float> >("float2")! .field("first", &std::pair<float,float>::first )! .field("second", &std::pair<float,float>::second );! function( "create_float2", &std::make_pair< float, float > );! } var i = Module.create_float2( 2.0, 3.0 );! i.first = 5;
  16. C++ͷΫϥεʹJavaScript͔ΒΞΫηε͢Δ #include <emscripten/bind.h>! class Hoge {! const int a;! public:!

    Hoge( int a_ ) : a( a_ ) {}! int run( int b ) { return a * b; }! };! using namespace emscripten;! EMSCRIPTEN_BINDINGS(mod) {! class_<Hoge>("Hoge")! .constructor<int>()! .function("run", &Hoge::run );! } var hoge = new Module.Hoge( 3 );! var result = hoge.run( 4 );! // result = 12
  17. C++ͷΫϥεʹJavaScript͔ΒΞΫηε͢Δ #include <emscripten/bind.h>! class Fuga {! int a;! public:! Fuga(

    int a_ ) : a( a_ ) {}! void set( int a_ ) { a = a_; }! int get() const { return a*2; }! };! using namespace emscripten;! EMSCRIPTEN_BINDINGS(mod) {! class_<Fuga>("Fuga")! .constructor<int>()! .property( "x", &Fuga::get, &Fuga::set );! } var fuga = new Module.Fuga( 3 );! fuga.a = 5;! var result = fuga.a;! // result = 10
  18. Emscriptenͷώʔϓ var fuga =! new Module.Fuga ώʔϓͷ ΞυϨεۭؒ 'VHBͷΞυϨε 'VHB

    GVHB 'VHBͷΞυϨε JavaScript C++ fuga.delete() delete 'VHBͷΞυϨε fuga.run() 'VHBͷΞυϨε ->run() ΦϒδΣΫτ͸ഁغ͢ΔલʹEFMFUF
  19. Emscriptenͷώʔϓ 'VHB ώʔϓͷ ΞυϨεۭؒ 'VHBͷΞυϨε var fuga =! new Module.Fuga

    GVHB 'VHBͷΞυϨε fuga.run() JavaScript C++ 'VHBͷΞυϨε ->run() ͜͜ͷΞυϨεͷอ࣋ʹ εϚʔτϙΠϯλΛ࢖͍͍ͨ var fuga = new Module.Fuga();! Module.do_something( fuga );! // C++ͷؔ਺ʹϙΠϯλ౉͠! fuga.delete();! // ຊ౰ʹ͜͜Ͱdeleteͯ͠΋େৎ෉?
  20. struct Piyo {! int run( int a ) { return

    a + 3; }! };! using namespace emscripten;! EMSCRIPTEN_BINDINGS(piyo) {! class_<Piyo>("Piyo")! // class PiyoΛόΠϯυ! .smart_ptr<std::shared_ptr<Piyo>>()! // std::shared_ptrΛ࢖ͬͯͶ! .constructor<>()! // Ҿ਺ແ͠ͷίϯετϥΫλΛόΠϯυ! .function( "run", &Piyo::run );! // ϝϯόؔ਺runΛόΠϯυ! }
  21. void do_something( std::shared_ptr< Piyo > piyo ) {! ...! }

    var piyo = Module.get_piyo();! Module.do_something( piyo );! piyo.delete();! // ͜͜Ͱ͸ࢀরΧ΢ϯτ͕1ݮΔ͚ͩ std::shared_ptr< Piyo > get_piyo() {! return std::shared_ptr< Piyo >( new Piyo() );! }
  22. EMSCRIPTEN_BINDINGS(piyo) {! class_<Piyo>("Piyo")! .smart_ptr_constructor(! &std::make_shared< Piyo >! )! // JavaScriptଆʹstd::shared_ptrͳ!

    // ΦϒδΣΫτΛฦ͢ίϯετϥΫλ! .function( "run", &Piyo::run );! } var piyo = new Module.Piyo();! Module.do_something( piyo );! piyo.delete();! // ͜͜Ͱ͸ࢀরΧ΢ϯτ͕1ݮΔ͚ͩ
  23. ϥΠϒϥϦΛ࢖͍ͨ͘ͳͬͨΒ $ ../emscripten/emconfigure ./configure --prefix=(ུ)! $ ../emscripten/emmake make! $ ../emscripten/emmake

    make install --7.தؒίʔυͷঢ়ଶͷϥΠϒϥϦ͕ੜ੒͞ΕΔ $ ../emscripten/em++ hoge.cpp -o hoge.bc! $ llvm-link hoge.bc libz.so.1.2.8 -o linked.bc! $ ../emscripten/em++ composite.bc -o hoge.html LLVM LinkerͰϦϯΫ͠Α͏
  24. #include <iostream> #include <boost/filesystem/path.hpp> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/fstream.hpp> int main()

    { boost::filesystem::directory_iterator iter( boost::filesystem::path( "/dev" ) ); const boost::filesystem::directory_iterator end; for( ; iter != end; ++iter ) std::cout << iter->path() << std::endl; } ͜ΜͳίʔυΛॻ͍ͯ΋
  25. Canvas͸SDLͷϑϨʔϜόοϑΝͩ #include <SDL/SDL.h> ͯ͠ SDL_Init( SDL_INIT_VIDEO ); ॳظԽ͠ screen =

    SDL_SetVideoMode(512, 512, 32, SDL_SWSURFACE ); ιϑτ΢ΣΞαʔϑΣεΛ࡞Ε͹
  26. Ի

  27. OpenAL͕WebALʹϚοϓ͞Ε͍ͯΔ #include <AL/al.h> #include <AL/alc.h> ͯ͠ device = alcOpenDevice (

    NULL ); context = alcCreateContext( device, NULL ); ίϯςΩετΛ࡞ͬͯ alGenSources( 1, &source ); alGenBuffers( 1, &buffer ); alBufferData( buffer, AL_FORMAT_MONO16, brass, 76833 * 2, 48000 ); alSourceQueueBuffers( source, 1, &buffer ); ιʔεͱόοϑΝΛ࡞ͬͯ ೾ܗσʔλΛॻ͖ࠐΜͰ alSourcePlay( source ); ࠶ੜ͢Δͱ
  28. OpenGL͕WebGLʹϚοϓ͞Ε͍ͯΔ OpenGL ESʹ؆୯ʹஔ͖׵͑ΒΕΔͱ͜Ζ͚ͩ std::cout << glGetString( GL_VERSION ) << std::endl;

    WebGL 1.0 (OpenGL ES 2.0 Chromium) WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work ܯࠂEmscriptenͷ(-ΤϛϡϨʔγϣϯΛ࢖༻தɻ ͜Ε͸·ͩ࡞Γ͔͚ͩʂಈ͘ͱࢥ͏ͳΑʂ WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports ܯࠂEmscriptenͷ(-JNNFEJBUFϞʔυͷ ΤϛϡϨʔγϣϯΛ࢖༻தɻ ͜ͷ෦෼ͷαϙʔτঢ়گ͸ۃΊͯݶఆతͩʂ ωΠςΟϒͰಈ͍ͯΔͷʹϒϥ΢βͰಈ͔ͳ͍ͱ͖͸ Emscriptenͷ࠷৽൛ΛQVMMͯ͘͠Δͱಈ͘ࣄ͕ Α͋͘Δ
  29. ࠷৽൛Ͱ΋όάͬͯΔ͜ͱ͕ Α͋͘Δ OpenGL͕௖఺ΠϯσοΫεʹ࢖͑Δܕ CJU੔਺ CJU੔਺ CJU੔਺ OpenGL ES͕௖఺ΠϯσοΫεʹ࢖͑Δܕ CJU੔਺ CJU੔਺

    EmscriptenͷOpenGLΤϛϡϨʔγϣϯʹ CJU௖఺ΠϯσοΫεΛ৯΂ͤ͞Δͱ CJUͬͯݴͬͯΔͷʹCJU۠੾ΓͰಡΜͰ͘ΕΔ
  30. try {! throw hoge();! } catch( const hoge& ) {!

    std::cerr << “oops” << std::endl;! } ͦͷଞ
  31. ྫ֎͕ඈΜͩ࣌ʹඈΜͩྫ֎Λදࣔ͢Δ -s EXCEPTION_DEBUG=1 ؔ਺ͷग़ೖΓΛදࣔ͢Δ -s LABEL_DEBUG=1 -s CORRUPTION_CHECK=1 όοϑΝΦʔόʔϥϯΛݕग़͢Δ -s

    SAFE_HEAP=1 ൣғ֎ࢀরٴͼΞϥΠϯϝϯτͷෆҰகΛݕग़͢Δ -s ASSERTIONS=1 ͳΜ͔࣮ߦ࣌ͷνΣοΫ͕૿͑Δ ৄࡉෆ໌