Phoenixで簡単なHTTPサーバー

仕事でElixirを使い始めました。Phoenixを調べています。

Phoenixのプロジェクトでデフォルトで作られるのはWebサイト的なアプリケーションですが、ビューはいらないので、デフォルトで作られたものをいじって、POSTでリクエストを受け取ってレスポンスを返すだけの簡単なサーバーにしました。

まず、コントローラを切り替えるweb/router.ex。デフォルトではブラウザからGETリクエストしか来ないようにCSRFトークンの検証とかをしていますが、これはいらないので削除します。

# web/router.ex
defmodule HelloPhoenix.Router do
  use HelloPhoenix.Web, :router

  # 1. これいらない
  # pipeline :browser do
  #   plug :accepts, ["html"]
  #   plug :fetch_session
  #   plug :fetch_flash
  #   plug :protect_from_forgery
  #   plug :put_secure_browser_headers
  # end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloPhoenix do
    # 2. :browserから:apiに変更
    pipe_through :api

    # 3. postも受け取れるようにする
    get "/", PageController, :index
    post "/hello", PageController, :hello
  end
end

次に、コントローラ側 web/controllers/page_controller.ex。HTMLをレンダリングする代わりに、レスポンスでJSONを返すようにします。

defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  def index(conn, _params) do
    conn |> json(%{key: "pong"})
  end

  def hello(conn, _params) do
    conn |> json(%{key: "pong"})
  end
end

あとはHTTPクライアントからアクセスしてみます。これは何を使ってもいいですが、Ruby標準のNet::HTTPを使いました。

require 'net/http'

# POST
uri = URI('http://localhost:4000/hello')
res = Net::HTTP.post_form(uri, 'key' => 'ping')
puts res.body # => {"key":"pong"}

# GET
uri = URI('http://localhost:4000')
puts Net::HTTP.get(uri) # => {"key":"pong"}

動きました。

C++1z INVOKEコンセプトに従った関数呼び出しをするinvoke()関数

C++11から入ったstd::mem_fn()関数、std::bind()関数、std::result_ofメタ関数などは、どれもINVOKEコンセプトという仕様のもとに定義されています。

これらの機能は、関数オブジェクトをただ呼び出すだけでなく、

  • メンバ関数ポインタ + レシーバー + 引数を指定してメンバ関数を呼び出したり、
  • メンバ変数ポインタ + レシーバーを指定してメンバ変数を取り出したり

といったことができます。そういった機能がINVOKEコンセプトというもので共通化されています。

C++1zでは、INVOKEコンセプトで関数呼び出しを行うstd::invoke()関数が導入されます。

#include <iostream>
#include <functional>

void f(int, char, double)
{
    std::cout << "f" << std::endl;
}

struct Functor {
    void operator()(int, char, double)
    {
        std::cout << "functor" << std::endl;
    }
};

struct X {
    int member_variable = 3;

    void member_function(int, char, double)
    {
        std::cout << "member_function" << std::endl;
    }
};

int main()
{
    // 関数
    std::invoke(f, 1, 'a', 3.14);

    // 関数オブジェクト
    std::invoke(Functor(), 1, 'a', 3.14);

    // メンバ変数
    X x;
    std::cout << std::invoke(&X::member_variable, x) << std::endl;

    // メンバ関数
    std::invoke(&X::member_function, x, 1, 'a', 3.14);
}

出力:

f
functor
3
member_function

宣言

// <functional>
namespace std {
    template <class F, class... Args>
    result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。