C++1z 数学の特殊関数

C++1zでは、C++11のころから繰り越されていた数学の特殊関数(special functions)が導入されます。

一覧としては、以下になります:

関数名 説明
assoc_laguerre ラゲールの陪多項式
associated Laguerre polynomials
assoc_legendre ルジャンドルの陪多項式
associated Legendre polynomials
beta ベータ関数
beta function
comp_ellint_1 (完全)第1種楕円積分
(complete) elliptic integral of the first kind
comp_ellint_2 (完全)第2種楕円積分
(complete) elliptic integral of the second kind
comp_ellint_3 (完全)第3種楕円積分
(complete) elliptic integral of the third kind
cyl_bessel_j (第1種)円筒ベッセル関数
cylindrical Bessel functions (of the first kind)
cyl_bessel_i 正規変形円筒ベッセル関数
regular modified cylindrical Bessel functions
cyl_bessel_k 非正規変形円筒ベッセル関数
irregular modified cylindrical Bessel functions
cyl_neumann 円筒ノイマン関数 (第2種円筒ベッセル関数)
cylindrical Neumann functions, cylindrical Bessel functions (of the second kind)
ellint_1 (不完全)第1種楕円積分
(incomplete) elliptic integral of the first kind
ellint_2 (不完全)第2種楕円積分
(incomplete) elliptic integral of the second kind
ellint_3 (不完全)第3種楕円積分
(incomplete) elliptic integral of the third kind
expint 指数積分
exponential integral
hermite エルミート多項式
Hermite polynomials
legendre ラゲールの多項式
Laguerre polynomials
laguerre ルジャンドル多項式
Legendre polynomials
riemann_zeta リーマンゼータ関数
Riemann zeta function
sph_bessel (第1種)球ベッセル関数
spherical Bessel functions (of the first kind)
sph_legendre 球ラゲール陪多項式
spherical associated Legendre functions
sph_neumann ノイマン関数 (第2種球ベッセル関数)
spherical Neumann functions, spherical Bessel functions (of the second kind)

これらの関数は、<cmath>ヘッダに追加されます。C言語の標準ライブラリにこれらの関数はないので、Cへの参照ではなく、C++の標準ライブラリとして動作が定義されます。

これらのほかに、float版として末尾にfがついたバージョン、long double版として末尾にlがついたバージョンが定義されます。

参照

お断り

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

C++1z false sharingとtrue sharingの制御

C++1zから、並行プログラミングで問題になりうる、キャッシュの無効化問題を制御できるようになります。

false sharingの制御

struct keep_apart {
    atomic<int> cat;
    atomic<int> dog;
};

このような構造体がある場合、catdogが同じキャッシュラインに乗ることがあります。スレッド1ではcat変数、スレッド2ではdog変数を操作するような状況で、それぞれが共通のキャッシュを無効化してしまうパフォーマンス劣化の問題が起こりえます。こういった状況をfalse sharingと言いますが、これを回避するために、<new>ヘッダにstd::hardware_destructive_interference_sizeという定数が定義されます。(「破壊的な干渉サイズ」といったところでしょうか)

この定数を構造体メンバのアライメントとして指定することによって、それぞれのメンバが独立したキャッシュラインに乗り、false sharingが回避できるようになります。

struct keep_apart {
    alignas(hardware_destructive_interference_size) atomic<int> cat;
    alignas(hardware_destructive_interference_size) atomic<int> dog;
};

true sharingの制御

true sharingという用語は一般的ではないようですが、意図的に複数のメンバを同一のキャッシュラインに乗せることがそう呼ばれます。

そのようなことをするために、同ヘッダにstd::hardware_constructive_interference_sizeという定数が定義されます。構造体全体に対してこの定数をアライメント指定することによって、構造体全体が同一キャッシュラインに乗ることが期待できるようになります。

struct alignas(hardware_constructive_interference_size) together {
    atomic<int> dog;
    int puppy;
};

宣言

// <new>
namespace std {
  static constexpr size_t hardware_destructive_interference_size = 実装定義;
  static constexpr size_t hardware_constructive_interference_size = 実装定義;
}

備考

私は並行プログラミングの専門家ではないこともあって、使用している用語や説明に間違いがあるかもしれません。そういったことがあったらご指摘いただけると助かります。

参照

お断り

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

C++1z 値を範囲内に収めるclamp()関数

値を、min <= v <= maxのように範囲内に収める関数として、clamp()が定義されます。

std::min(std::max(min_value, x), max_value)のようにするのと同等の効果があります。

引数の順番は、対象の値、最小値、最大値です。戻り値として範囲内に丸められた値が返ります。

// <algorithm>
namespace std {
  template <class T>
  constexpr const T& clamp(const T& v, const T& lo, const T& hi);

  template <class T, class Compare>
  constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp);
}

例:

#include <iostream>
#include <algorithm>

int main()
{
    int v = 3;

    // vの値を[0, 2]の範囲内に収める
    int result = std::clamp(v, 0, 2);
    std::cout << result << std::endl;
}

出力:

2

参照

お断り

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

C++1z swap可能かを判定する型特性クラス

swap操作をラップした関数のnoexcept条件を書きやすくすることを主な目的として、C++1zから以下の型特性クラスが追加されます。

  • Tswap可能かを判定するis_swappable<T>
  • T型Uの組み合わせでswap可能かを判定するis_swappable_with<T, U>
  • 例外を送出せずにswap可能かを判定するis_nothrow_swappable<T>is_nothrow_swappable_with<T, U>
  • この4つの変数テンプレート版

たとえば、これまで以下のように宣言されていた組み込み配列版のswap関数は、

// <utility>
namespace std {
  template <class T, size_t N>
  void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b)));
}

C++1zでは以下のような宣言になります。

// <utility>
namespace std {
  template <class T, size_t N>
  void swap(T (&a)[N], T (&b)[N]) noexcept(is_nothrow_swappable<T>::value);
}

宣言

// <type_traits>
namespace std {
  template <class T, class U> struct is_swappable_with;
  template <class T> struct is_swappable;

  template <class T, class U> struct is_nothrow_swappable_with;
  template <class T> struct is_nothrow_swappable;

  // 変数テンプレート版
  template <class T, class U>
  constexpr bool is_swappable_with_v
    = is_swappable_with<T, U>::value;
  template <class T>
  constexpr bool is_swappable_v
    = is_swappable<T>::value;

  template <class T, class U>
  constexpr bool is_nothrow_swappable_with_v
    = is_nothrow_swappable_with<T, U>::value;
  template <class T>
  constexpr bool is_nothrow_swappable_v
    = is_nothrow_swappable<T>::value;
}

参照

お断り

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

C++1z 現在発生している例外の数を取得するuncaught_exceptions()関数

C++98時点で、捕捉していない例外があるかどうかを判定するstd::uncaught_exception()関数が定義されていました。この関数は、例外が送出されてから捕捉されるまでの間でtrueを返します。

C++1zでは、捕捉していない例外がいくつあるかを返すstd::uncaught_exceptions()関数が定義されます。

// <exception>
namespace std {
  int uncaught_exceptions() noexcept;
}

この関数は、トランザクション処理で使用します。以下のように例外処理が入れ子になっている場合、

RAII1 raii1;
try {
    RAII2 raii2;
    try {
        <next>
    }
    catch (...) {
        <rollback>
        throw;
    }
}
catch (...) {
    <rollback>
    throw;
}

「予定していた例外の数よりも多くの例外が送出されていたら、処理の失敗とみなしてロールバック処理を行う」というようなことが必要となります。そのような状況でこの関数を使用します。

参照

お断り

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

「プログラマに必要なことを学ぶ会」を開催しました

数年前から、少人数でフリーテーマの勉強会をしたいと思っていました。私は普段、プログラミング関係の技術強化と、仕事全般で使用するような技術としての資質強化を交互にやってきていたのですが、資質強化の方はいろいろ身につけても話す場がなかったので、Boost.勉強会とはべつで新しい勉強会を始めることにしました。

少人数の勉強会にしたのは、参加者みんなの話を聞きたかったからです。Boost.勉強会は(私の意図とちがって)100人とかがポンと集まってしまうので、なかなか難しいです。

少人数の勉強会で課題となるのは、参加したいと思ってくれる潜在的な参加者のうち、本当に参加したいと強く思っている人が参加できるようにすることです。IT系の勉強会は一時期すごく流行って、いまだいぶ落ち着いてきましたが、それでも参加募集を開始すると一気に人が集まるところは多々目にします。そういった参加登録者のなかには「実際に行くかどうかはわからないけど、とりあえず席だけ取っておこう」という軽い気持ちで参加登録し、それによって本当に参加したいと思っている人が来れない、といった問題も起きていました。

そういった問題に対処するために、参加のハードルを少し上げることにしました。そのハードルの内容は参加募集のページを見ていただければわかります。結果的にすごくモチベーションのある人ばかりが集まり、白熱した議論が起こり、私の意図を十分満たす形で第一回を終えることができました。参加者層も、新卒の方、ゲーム系の方、システム・Web系の方、受託開発の方、請負開発の方、教職の方など、幅広いジャンルの人が集まっていました。

今回は、参加募集10人 + 運営者2人という人数で開催しましたが、2時間くらいの勉強会で、全員の話を十分に聞けるようにするには、これくらいの規模が上限のような気がしています。潜在的に参加したいと思ってくれている方が多くいるのは把握しているつもりですが、それを受け止めきれないのは、まだモヤモヤしているところです。

どれくらいこの勉強会が続くかはまだわからないですが、ひとまず第二回はなんとか開催できそうです。直近の私の課題としては、議論のスムーズな運営をするスキルをもうちょっと向上させたいですね。

Boost.勉強会 #20 東京をやります

2016年07月23日に、IIJさんの会場をお借りして、Boost.勉強会を開催します。

今回私は、C++1zについて話す予定です。
皆さまのご参加をお待ちしております。

C++1z atomic<T>::is_always_lock_free

C++11時点では、atomic<T>クラステンプレートの非静的メンバ関数としてis_lock_free()が用意されていました。この関数は、T型のatomicオブジェクトがロックフリーに振る舞うかどうかをbool値で返します。この関数が返す値はTの型ごとに静的な値ですが、atomicクラスのオブジェクトを作らないと、そのプロパティを参照できませんでした。

Tがロックフリーに振る舞えるかどうかによって、ユーザーが使用するアルゴリズムが変わる場合があり、そういった状況ではロックフリーに振る舞えるかどうかを静的に取得できるとSFINAEなどを使ってコンパイル時にアルゴリズムの選択ができて便利です。

<atomic>ヘッダで定義されるATOMIC_INT_LOCK_FREEのようなマクロで、型Tがロックフリーに振る舞えるかどうかを静的に取得できるので、それをラップして使いやすくした機能をC++1zで用意することになりました。

C++1zでは、atomicクラステンプレートに、is_always_lock_freeという名前の静的メンバ定数が定義されます。

namespace std {
    template <class T>
    class atomic {
    public:
        static constexpr bool is_always_lock_free = 実装定義;
    };
}

従来のis_lock_free()メンバ関数は、非推奨にはならないようです。

参照

お断り

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

C++1z コンテナに不完全型の最小サポートを追加

定義が完了していない不完全型(incomplete type)はsizeofがとれないので、アロケータとそれを使用するコンテナの実装が少し難しくなる場合があります。ですが、自分自身の型のオブジェクトがいくつあるかカウントする、というような場合に以下のようなコードを書くことになり、不完全型をコンテナの要素に指定できると便利です。

struct Entry {
    std::list<Entry> messages;
    // …
};

C++1zでは、以下の標準コンテナに限り、不完全型を要素とすることが許可されます:

  • std::vector
  • std::list
  • std::forward_list

ただし、これらのコンテナのなんらかのメンバ関数を呼び出す前には、要素型が完全型になっている必要があります。

なお、Boost Container Libraryでは1.61.0現在、static_vectorbasic_string以外の全てのコンテナに不完全型のサポートが入っています。(Unordered連想コンテナ、Multi-IndexといったBoost.Containerに含まれないBoostのコンテナは、その限りではありません)

参照

お断り

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

C++1z ファイルシステムライブラリ

C++1zでは、ファイルパス、ディレクトリ、ファイルのコピー・移動などを扱うファイルシステムライブラリが導入されます。

このライブラリは、Boost Filesystem Library v3をベースにしています。basic_path<CharT>のように文字型をパラメータとするのではなく、pathクラスが用意され、内部で文字エンコーディングが行われます。

C++1zのファイルシステムライブラリが議論されていくにつれてBoost.Filesystemも追従して更新されていますので、Boost 1.61.0現在では、この2つの差異は小さくなっています。

ファイルシステムライブラリの機能は、<filesystem>ヘッダのstd::filesystem名前空間で定義されます。

#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path p1 = "C:/";
    fs::path p2 = "a.txt";

    // operator/()で2つのパスを連結する
    // operator+=(), concat()メンバ関数を使用してもよい
    fs::path p = p1 / p2;

    // ファイルをfromからtoにコピーする
    // エラーが発生した場合は、std::filesystem::filesystem_error例外が送出される
    fs::path from = p;
    fs::path to = "C:/b.txt";
    fs::copy_file(from, to);
}

エラー報告

ファイルシステムライブラリのエラー報告は、std::filesystem::filesystem_error例外を送出するか、std::error_codeオブジェクトを返すかのどちらかを、ユーザーが選択できます。

ファイルコピーの場合は、以下のようになります:

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path from = "C:/a.txt";
    fs::path to = "C:/b.txt";

    // デフォルトでは、エラー発生時に例外を送出する
    try {
        fs::copy_file(from, to);
    }
    catch (fs::filesystem_error& e) {
        std::cout << e.what() << std::endl;
        throw;
    }

    // 最後の引数にerror_codeオブジェクトへの参照を渡すと、
    // 例外送出の代わりにerror_codeオブジェクトにエラー情報が格納される
    std::error_code error;
    fs::copy_file(from, to, error);
    if (error) {
        std::cout << error.message() << std::endl;
    }
}

文字エンコーディング

std::filesystem::pathクラスは、文字コード関係の機能が内部に入っており、OSネイティブの文字コードでファイルパス文字列を取得したり、自分がほしい文字コードでファイルパス文字列を取得できたりします。

native()メンバ関数は、OSネイティブの文字コードでファイルパス文字列のstd::basic_stringを返します。文字の型は、POSIXベースのOSではcharWindowsではwchar_tになります。文字コードもOSネイティブのものになります。

// Windowsの場合
path p = "a.txt";
path::string_type str = p.native(); // string_typeはstd::wstring型
// POSIXベースOSの場合
path p = "a.txt";
path::string_type str = p.native(); // string_typeはstd::string型

文字コードを指定したい場合は、以下のメンバ関数を使用します。

メンバ関数 文字コード
std::string string() const OSネイティブのマルチバイ文字コード
std::wstring wstring() const OSネイティブのワイド文字コード
std::string u8string() const UTF-8
std::u16string u16string() const UTF-16
std::u32string u32string() const UTF-32

Unicodeの正規化に関する規定はとくにありません。

パスの汎用フォーマット

パスのフォーマットには、たとえばWindows"C:\\a.txt"ようにバックスラッシュでディレクトリを区切るものと、"C:/a.txt"のようにスラッシュで区切るものがあり、後者をこのライブラリでは汎用パスフォーマット(generic path format)と呼んでいます。

汎用フォーマットでパス文字列を取得したい場合に、以下のメンバ関数が使用できます。

template <class EcharT, class traits = char_traits<EcharT>,
          class Allocator = allocator<EcharT> >
basic_string<EcharT, traits, Allocator>
  generic_string(const Allocator& a = Allocator()) const;

std::string generic_string() const;
std::wstring generic_wstring() const;
std::string generic_u8string() const;
std::u16string generic_u16string() const;
std::u32string generic_u32string() const;

文字コードは、前述したものと同様です。

Boost.Filesystemとの差異

Boost 1.61.0のBoost.Filesystemライブラリと比較して、設計として以下の差異があります。

日時を表す型が、Boost.Filesystemライブラリではstd::time_tですが、標準の方はstd::chrono::time_point<trivially-clock>となっています。Clockの型は実装定義です。日時は、ファイルの最終日時を取得・設定する際に使用します。

ロケールの扱いが少し違います。Boost.Filesystemのpathクラスはimbue()メンバ関数を持っていますが、標準の方はありません。Boost.Filesystemのpathクラスはコンストラクタでcodecvtオブジェクトをとりますが、標準の方はlocaleオブジェクトをとります。 私が少し調べて気づいたのはこれくらいです。細かい機能のありなしはありますが、設計として異なるのはこれくらいでしょう。

参照

お断り

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