C++ & Lua : 任意の引数群と return 群の Lua の関数を C++ から簡単に扱えるヘルパーライブラリーの作り方

# 目的

C++ コードから Lua の関数を Lua の C API をちまちま操作して呼び出すのは少々面倒くさい。そこで任意の引数群と return 群の Lua の関数を C++ から簡単に扱えるヘルパーライブラリーを用意したい。

## 望まれる成果

次のような Lua コード: 

```lua
function hoge( a, b )
  return a + b, a - b, a / b, a .. b
end
```

が有効な状況で、次のような C++ コードからの Lua 関数の呼び出しと結果の取得を行い:

```cpp
// Lua 関数の return 群の型はテンプレートで与える
// Lua 関数の引数群は関数名に続けて任意の型の値をほいほい並べて渡したい
// 例示の実引数 lua_state は `lua_State*` 型のオブジェクト
// 例示の実引数 `"hoge"` は Lua の関数名、 `123` は Lua 関数の第1実引数、 `456` は Lua 関数の第2実引数
// Lua 関数の実行結果は `returns` にさらりと取得したい
const auto returns = usagi::lua::pcall< int, int, float, std::string >( lua_state, "hoge", 123, 456 );
```

次のように計算結果を `usagi::lua::pcall` の戻り値から出力を行いたい:

```
579 <- int : Lua 関数の return 群の 1 つめ
-333 <- int : Lua 関数の return 群の 2 つめ
0.269737 <- float : Lua 関数の return 群の 3 つめ
123456 <- std::string : Lua 関数の return 群の 4 つめ
```

この様に扱える `usagi::lua::pcall` をヘルパーライブラリーとして用意したい。

# 作り方

- 先に [「C++14 : 任意のタプルへ任意のファンクターを適用する apply( my_tuple, my_functor ) の作り方」]() で紹介した `apply` も使う。そちらは `usagi::tuple::apply` としてライブラリー化してあり include するものとする。
- なお、この例はあくまでもこのようなライブラリーの作り方を紹介する目的であり、完全なライブラリーを提供する目的ではないので、 `usagi::lua::pcall` のほかに実用上各種型について特殊化して取り揃える事になろう `usagi::lua::push_stack` や `usagi::lua::peek_stack` については今回の例示に必要最小限の特殊化のみ示す事とした。

```cpp
#pragma once

#include <usagi/tuple/apply.hxx>

namespace usagi
{
  namespace lua
  {
    template < typename T > auto push_stack( lua_State*, T ) { throw std::logic_error( "push_stack: not supported type." ); }
    template < > auto push_stack< int >( lua_State* lua_state, int value ) { lua_pushinteger( lua_state, value ); }
    
    template < typename T >
    auto peek_stack( lua_State*, const int = 1 ) { throw std::logic_error( "peek_stack: not supported type." ); }
    template < > auto peek_stack< int >( lua_State* lua_state, const int index ) { return static_cast< int > ( lua_tointeger( lua_state, index ) ); }
    template < > auto peek_stack< float >( lua_State* lua_state, const int index ) { return static_cast< float > ( lua_tonumber( lua_state, index ) ); }
    template < > auto peek_stack< std::string >( lua_State* lua_state, const int index ) { return std::string( lua_tostring( lua_state, index ) ); }
    
    template < typename ... OUT_TYPES,  typename ... IN_TYPES >
    auto pcall
    ( lua_State* lua_state
    , const std::string& symbol
    , IN_TYPES&& ... in_values
    ) -> std::tuple< OUT_TYPES ... >
    {
      using in_tuple_type  = std::tuple< IN_TYPES ... >;
      using out_tuple_type = std::tuple< OUT_TYPES ... >;

      lua_getglobal( lua_state, symbol.c_str() );

      usagi::tuple::apply
      ( std::forward_as_tuple( in_values ... )
      , [ = ]( auto&& value ) { push_stack( lua_state, value ); }
      );
      
      constexpr auto in_size  = static_cast< int >( std::tuple_size< in_tuple_type >::value );
      constexpr auto out_size = static_cast< int >( std::tuple_size< out_tuple_type >::value );
      
      if ( const auto r = lua_pcall( lua_state, in_size, out_size, 0 ) )
        throw std::runtime_error( "lua pcall `" + symbol + "` failed. return code is " + std::to_string( r ) );
      
      out_tuple_type out;
      int index = -out_size;
      usagi::tuple::apply( out, [ =, &index ]( auto& value ) { value = peek_stack< std::remove_reference_t< decltype( value ) > >( lua_state, index++ ); } );
      
      lua_pop( lua_state, out_size );
      
      return out;
    }
    
  }
}
```

## 実行結果

```cpp
auto main() -> int
{
  auto lua_state = luaL_newstate();
  
  luaL_dostring
  ( lua_state
  , u8R"(
      function hoge( a, b )
        return a + b, a - b, a / b, a .. b
      end
    )"
  );
  
  auto r = usagi::lua::pcall< int, int, float, std::string >( lua_state, "hoge", 123, 456 );
  
  usagi::tuple::apply( r, []( auto value ) { std::cout << "out : " << value << " " << typeid( value ).name() << "\n"; } );
  
  lua_close( lua_state );
}
```

```
out : 579 i
out : -333 i
out : 0.269737 f
out : 123456 NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
```

# Wandbox

- http://melpon.org/wandbox/permlink/BzNHWWfAZxp7TGXu

# ポイント

- Lua 関数の引数群と return 群を `std::tuple` でそれぞれまとめて扱っている。
   - 提供する `usagi::lua::pcall` の引数や型引数でわざわざ `std::tuple` をユーザーがちまちま書くのはだるいため、 variadic template でよしなにそんな必要が無いインターフェースを提供している。型引数に複数の variadic template があるが、 Lua 関数の引数群は実引数で必ず与えて使う事になるので、 return 群の variadic template の型引数とは自動的に区別して扱える。
       - よって return 群の型引数も `usagi::lua::pcall` を使うユーザーコード側では std::tuple を意識せずに呼び出しを記述できる。
- こんなこともあろうかと事前に用意しておいた `usagi::tuple::apply` べんり。

## 注意

実用する場合には例示のため端折った `usagi::lua::push_stack` や `usagi::lua::peek_stack` のテンプレート特殊化を `std::uint8_t` からきっちり全部書いたり、そもそも `lua_State*` をシャーディングして並行処理できるようにしたりとかいろいろと工夫したライブラリーが必要になります。気が向いたらというか時間がまたとれたらちまちま切り出して紹介しようと思います。