Boost.HOFについて
Boost 1.67.0から Boost.HOF というライブラリが追加されました。 HOFはHigher Order Functions(高階関数)の略で、関数や関数オブジェクトのユーティリティを提供するライブラリです。 ヘッダーオンリーでC++11以上の環境で使えます。 3つのコンポーネントが含まれています。
- Function Adaptors and Decorators: 既存の関数を強化するアダプターやデコレーターです。
- Functions: 特定の目的のための関数です。
- Utilities: 関数定義や使用時に使える汎用的なユーティリティです。
この記事では一部の機能を紹介します。
Function Adapters and Decorators
Funcition Adaptors
Function Adaptersは関数を受け取り、その関数をもとに新しい関数を返します。
lazy
lazyはstd::bindのように使うことが出来るアダプターです。std::bindと異なるのはconstexprでも利用することが出来る点です。 サンプルはaddの第2引数を3で束縛した関数を返します。
#include <boost/hof.hpp> namespace hof = boost::hof; constexpr int add(int a, int b) { return a + b; } int main() { constexpr auto addThree = hof::lazy(add)(hof::_1, 3); static_assert(addThree(5) == 8); }
pipable
pipableは f(x, args...)
のような関数を x | f(args...)
の形で呼べるようにします。C++では operator.
はオーバーロードできませんが、 フリー関数をメンバ関数のようにするイメージです。 ( イメージ x(f)
-> x.f()
)
メソッドチェーンがスッキリします。 ( g(h(f(x)))
のような呼び出しが x | f() | h() | g()
のように変形できます)
サンプルはadded左辺がaddの第1引数になります。
#include <boost/hof.hpp> namespace hof = boost::hof; constexpr int add(int a, int b) { return a + b; } int main() { constexpr auto added = hof::pipable(add); constexpr auto result = 3 | added(5) | added(7); static_assert(result == 15); }
flow
flowは複数の関数を受け取りそれらの関数を順番に呼び出す関数を返します。1つ前の関数の戻り値が次の関数の引数になります。使い方によってはpipableと似たようなことができます。 サンプルは引数で渡された文字列をhtmlタグで囲んでいます。
#include <boost/hof.hpp> #include <iostream> #include <string> namespace hof = boost::hof; std::string addpre(std::string const& str) { return "<pre>" + str + "</pre>"; } std::string adddiv(std::string const& str) { return "<div>" + str + "</div>"; } int main() { auto wrap = hof::flow(addpre, adddiv, adddiv); std::cout << wrap("<span>aaa</span>") << std::endl; }
出力
<div><div><pre><span>aaa</span></pre></div></div>
infix
infixは2つ引数を取る関数を接中辞(中間記法とも)に書けるようにします。a + bの+のような二項演算子の演算子の位置のイメージです。具体的には f(x, y)
を x <f> y
の形式で呼べるようにします。
サンプルでは std::equal_to
を中間記法で呼べるようにします。
#include <boost/hof.hpp> #include <functional> namespace hof = boost::hof; int main() { constexpr auto equals = hof::infix(std::equal_to<>{}); constexpr auto a = 42; constexpr auto b = 42; static_assert(a <equals> b); }
proj
projはprojの第2引数に渡した関数を呼び出すときに、各引数に第1引数で渡した関数を呼び出してから渡します。 proj(p, f)(1, 2, 3) == f(p(1), p(2), p(3))
のようなイメージです。
#include <boost/hof.hpp> #include <iostream> namespace hof = boost::hof; int twice(int x) { return x * 2; } void print(int x, int y, int z) { std::cout << x << " " << y << " " << z; } int main() { constexpr auto twiced_print = hof::proj(twice, print); twiced_print(5, 7, 11); }
出力
10 14 22
Decorators
Decoratorsは関数を受け取り、Function Adapterを返す関数です。
repeat
repeatは指定された回数だけ、関数呼び出しをネストします。 サンプルでは初期値1から2倍にする関数を10回呼び出します。
#include <boost/hof.hpp> namespace hof = boost::hof; constexpr int twice(int x) { return x * 2; } int main() { constexpr auto pow_10 = hof::repeat(10)(twice); static_assert(pow_10(1) == 1024); }
if_
if_は第1引数に述語メタ関数を取り、それがtrue_typeの場合は第2引数で渡された関数を呼び出す関数を返します。false_typeの場合、関数呼び出しを行うとコンパイルエラーになるようにします。 サンプルのコメントアウトを外すとコンパイルエラーになります。trueの場合はtwiceがそのまま呼び出されます。
#include <boost/hof.hpp> #include <type_traits> namespace hof = boost::hof; constexpr int twice(int x) { return x * 2; } int main() { hof::if_(std::bool_constant<true>{})(twice)(1); // hof::if_(std::bool_constant<false>{})(twice)(1); }
Functions and Utilities
その他の便利関数を紹介します
always
alwaysは引数にとった値を返す関数を返します。この関数は任意の引数を取ることができますが戻り値は固定です。 サンプルは常に1を返す関数を作成しています。
#include <boost/hof.hpp> namespace hof = boost::hof; int main() { constexpr auto one = hof::always(1); static_assert(one() == 1); static_assert(one(1, 3.14, "hello") == 1); }
identity
identityは引数で渡された値をそのまま返す関数です。
#include <boost/hof.hpp> namespace hof = boost::hof; int main() { static_assert(hof::identity(42) == 42); }
construct
constructはテンプレートパラメータで渡された型のインスタンスを作成するファクトリ関数を返します。その関数の引数の値をコンストラクタの引数に渡します。 型だけでなくテンプレートテンプレートパラメータを渡すこともでき、その場合ファクトリ関数の引数からテンプレートパラメータが決まります。 サンプルではX型とクラステンプレートYのインスタンスを作成しています。
#include <boost/hof.hpp> namespace hof = boost::hof; struct X { X(int, double, char const*) {} }; template <typename T, typename U, typename V> struct Y { Y(T, U, V) {} }; int main() { const auto createX = hof::construct<X>(); createX(1, 3.14, "hello"); const auto createY = hof::construct<Y>(); createY(true, nullptr, 2.f); }
BOOST_HOF_LIFT
BOOST_HOF_LIFTは関数テンプレートや関数オブジェクトテンプレートやオーバーロード関数をジェネリックラムダにラップします。通常、関数テンプレートは他の高階関数に渡す場合、型を明示してインスタンス化しなければなりませんし、オーバーロード関数も同様にどのオーバーロードを渡すべきかキャストする必要があります。
イメージ例
std::vector<int> v;
があるとして、関数テンプレートの場合、
template <typename T> T my_add(T a, T b); ... std::accumulate(v.begin(), v,end(), 0, &my_sum<int>);
オーバーロード関数の場合
int my_add(int a, int b); double my_add(double a, double b); std::string my_add(std::string a, std::string b); ... std::accumulate(v.begin(), v.end(), static_cast<int (*)(int, int)>(my_add));
のようなことが必要です。BOOST_HOF_LIFTはジェネリックラムダでラップしてくれます。 [my_add](auto&&... args) { return my_add(std::forward<decltype(args)>(args...); }
のようなイメージです。
関数テンプレートもオーバーロード関数も以下のように書けます。
std::accumulate(v.begin(), v.end(), BOOST_HOF_LIFT(my_add));
tap
tapはpipableで使われることが想定されています。pipableの途中の値を見ることができます。
#include <boost/hof.hpp> #include <iostream> namespace hof = boost::hof; constexpr int add(int a, int b) { return a + b; } template <typename T> void print(char const* key, T const& value) { std::cout << key << ": " << value << std::endl; } int main() { auto added = hof::pipable(add); auto result = 3 | added(5) | hof::tap([](auto value) { print("temp", value); }) | added(7); print("result", result); }
出力
temp: 8 result: 15
以上 Boost.HOFの紹介でした。