2017-11-28

C++標準化委員会の文書: P0780R0-P0789R0

P0780R0: Pack expansion in lambda init-capture

lambda式のinit-captureにpack expansionを認める提案。ムーブができるようになる。

template < typename ... Types  >
void f( Types ... args )
{
    auto lambda = [ args = std::move(args)... ] { return g( args...) ; }
}

P0781R0: A Modern C++ Signature for Main

mainのシグネチャーを近代化する提案。

int main( const 何らかのコンテナー型<何らかの文字列型> args ) ;

にしたい。この提案では、std::initializer_list<std::string_view>を推奨している。

たしかに、そろそろmainも近代化されるべきだ。

P0782R0: A Case for Simplifying/Improving Natural Syntax Concepts

コンセプトを引数に取る関数テンプレートの文法を使った関数の依存名の解決は、コンセプトで示されているものしかできないようにしようという提案。

現在のコンセプトは、制約テンプレートの使用者に対するチェックだけで、制約テンプレート内のチェックはない。

例えば、現状のコンセプトは以下のコードが通る。

template < typename T >
concept has_f = requires( T x ) { x.f() ; }

void call_f( has_f & x )
{
    // コンセプトに書いてある
    x.f() ;
    // OK、ただしコンセプトに書いてない
    x.g() ;
}

struct X
{
    void f() ;
    void g() ;
}

int main()
{
    X x ;
    call_f( x ) ;
}

コンセプトによるチェックがないと、以下のようなコードが通ってしまう。

template < typename T >
concept has_const_f = requires( T x ) { const_cast<const T &>(x).f() ; }

void call_f( has_const_f & x )
{
    x.f() ;
}

struct X
{
    void f() ;
    void f() const ;
} ;

int main()
{
    X x ;
    // 非const版のvoid X::f()が呼ばれる
    f( x ) ;
}

コンセプトは制約テンプレートを制限しないので、このような挙動になってしまう。

文書では、初心者にコンセプトを使いやすいようにし混乱を防ぐために、ドラフト入りするときに削られたtarse notationによる関数制約テンプレートを復活させ、その依存名の解決はコンセプトに合致するものしか行わないようにしようという提案をしている。

tarse notationを使わないものと使うもので挙動が違うのはますます混乱の元ではないか。

P0783R0: P0783: Continuations without overcomplicating the future

現在、futureに継続を追加するための議論が行われているが、結局executorなしでは継続が実行される媒体が決定的ではなく意味がない。Facebookはfollyライブラリで継続とexecutorを持つfutureを実装して、社内で使った経験から、この件について意見している。

P0784R0: Standard containers and constexpr

vectorやunordered_mapのような可変サイズのコンテナーは実行時プログラミングに便利だ。ということは、コンパイル時計算にも便利なはずだ。

constexpr版のvectorやstringが提案されているが、vectorをそのままconstexr化することはできないのか。

vectorのconstexpr化を妨げる処理は3つ。

  1. デストラクター
  2. メモリ確保
  3. in-place new

デストラクターはconstexpr化できないが、この制約は取っ払うことができる。EDG, MSVC, GCC, Clangの開発者は皆同意している。

メモリ確保はコンパイラーがコンパイル時のメモリ確保を特別に処理することでconstexpr化できる。しかし、コンパイル時メモリ確保は、未定義の挙動を完全に検出できなければならないので、アドレスには追加のメタデータを付与しなければならない。これによりポインターのvoid *への変換はできなくなる。

問題は、いまのoperator newの宣言が以下のようになっていることだ。

void * operator new( std::size_t ) ;

ただし、new, deleteを使っているのならばコンパイラーが特別に対応することもできる。あるいは標準アロケーターで対処する。

in-place newをすべてconstexpr化することは無理だが、vectorで使う一部の用例はconstexpr化できる。

さて、コンパイル時計算で確保したメモリを開放しない場合どうなるのか。一番簡単な方法は未開放を禁止するということだ。しかし、コンパイル時処理の結果を実行時に渡したいときもあるだろう。すると開放されなかったコンパイル時メモリ確保はstaticストレージとして残すという手もある。

夢が広がる話だ。あらゆる計算はコンパイル時計算になりうる。

P0785R0: P0785R0: Runtime-sized arrays and a C++ wrapper

実行時サイズ配列復活論。CのVLAのサブセットとC++のラッパークラスの提案。

ようするにスタックから実行時に指定したサイズのメモリを確保したいということだ。

かつてC++14に入りそうだったdynarryは、スタックから確保できない場合、動的確保にフォールバックするという設計で、確実にスタックから確保されてほしい利用者にとって当てにできない設計であった。その後、厳格にスタックからしか確保しないフォールバックのないbs_arrayなども提案されたりしていた。

C++規格では、スタックとヒープという存在を避けてきた。代わりに、オブジェクトは寿命期間を持つストレージで区別されてきた。殆どのコンパイラーはスタックとヒープを使っていて、その使い分けは自然なものだ。しかし、世の中には組み込みとかカーネルなどの極端な環境があり、スタックメモリーのサイズが極端に小さい環境もあるかもしれない。そういう環境ではメモリはヒープに確保し、スタックではヒープメモリへのアドレスを格納するだけという実装もあり得る。結局、C++規格としてはスタックの利用を強制することはできない。

[PDF] P0786R0: ValuedOrError and ValueOrNone types

エラー処理を暗号的ではなくなるが冗長な記述になるライブラリの提案。

提案されているライブラリの一部を使うと、以下のように書ける。

// 同じ
x.has_value() ;
value_or_error::has_value(x) ;

// 同じ
(o) ? f(*o) : nullopt ;
value_or_error::transform(o, f) ;

// 同じ
(!e) ? e.error() : err
value_or_error::error_or(e, err)

// 同じ
(e) ? false : e.error() == err ;
value_or_error::check_error(e, err) ;

暗号的なコードではなくなるが、冗長な記述になるし、意味がわかりやすいかというとそれも疑問だ。

[PDF] P0787R0: Proclaimed Ownership Declarations

モジュールからproclaimed-ownership-declarationを削除する提案。

[PDF] P0788R0: Standard Library Specification in a Concepts and Contracts World

「プログラムの挙動に合わせて仕様を変えるのは、その逆より簡単だ」 Alan Perlis

「8.5×11インチの紙1枚に収まらない仕様は理解不可能だ」 Mark Ardis

「specというのは仕様(specification)の略か? 憶測(speculation)の略なんじゃないか?」 詠み人知らず

標準ライブラリはどのようにconceptに対応すべきかという方針の提案。

Requires: は現状維持。代わりに新しい要素を追加する。

Requires: を段階的に廃止し、新しい要素で置き換える。

新しい要素として、Constraitns:, Diagnostics:, Expects:, Ensures:を追加する。

[PDF] P0789R0: Range Adaptors and Utilities

Rangeアダプターの提案。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2017-11-22

C++標準化委員会の文書: P0770R0-P0779R0

P0770R0:A Proposal to Specify Behavior in Case of Exception Allocation Failure

例外オブジェクトのストレージは未規定の方法で確保されると規格にある。動的確保する実装の場合、確保に失敗した場合に対処する方法がない。

そこで、例外処理が失敗した場合に絶対に失敗せずに投げられる何らかの例外型を追加する提案。bad_allocを再利用するのがよいとしている。

[PDF] P0771R0: std::function move operations should be noexcept

std::functionのムーブ処理をnoexceptにする提案。すでにanyやshared_ptrはnoexceptなのでstd::functionをnoexceptにしない理由はない。

[PDF] P0772R0: Execution-Agent Local Storage

スレッドより軽い実行媒体がTLSを使うとスレッドのTLSが使われてしまう。ExecutorにTLSを要求するAPIを用意して、ExecutorがTLSを提供するか拒否するかして、スレッドより下の軽い実行媒体にもTLSを提供しようという提案。

P0773R0: Towards meaningful fancy pointers

ポインター風に振る舞うクラス型fancy pointerについての考察。ポインターにメタ情報を付加したり、アドレスにnear/far/segmentといった概念があったりする場合に必要。

[PDF] P0774R0: Module Declaration Location

module宣言の文法を変える提案。相変わらず文法が安定しない。

[PDF] P0775R0: Module Partitions

一つのソースファイルに複数のモジュールを書くことができるようにする提案。

P0776R0: Rebase the Parallelism TS onto the C++17 Standard

Parallerism TSはC++14の文面に合わせて書かれていたので、C++17の文面に合わせて書き直す提案。

[PDF] P0777R0: Treating Unnecessary decay

標準ライブラリでdecayを使っているがremove_cvrefですむ場所でremove_cvrefを使うよう書き換える提案。

[PDF] P0778R0: Module Names

モジュールが未だにこんな問題を抱えてるとは、C++20に入るのはおぼつかないのではないだろうか。

モジュールでimport foo ;と書くと、「これはモジュール名fooをimportしろ」という意味だ。ではモジュール名fooに対応するモジュールが定義されているソースファイルは何だろうか。規格は何も定めていない。

ヘッダーファイルでは、ヘッダー名は実装が実ファイルへのマッピングをするヘッダーか、直接物理のファイル名を意味している。ヘッダーを実装しているC++コンパイラーは存在せず、既存のすべてのコンパイラーはヘッダー名をファイル名として解釈し、所定のincludeディレクトリー群からヘッダー名のファイル名を探し出す。

モジュールの実装では、モジュール名、モジュールソースファイル、モジュールバイナリファイルが存在するはずだ。モジュールバイナリファイルは規格の規定するところではないが、モジュールソースファイルを処理した結果の何らかのファイルだ。モジュールの目的がコンパイルの高速化にあるので、当然このような実装になるはずだ。

すると、モジュール名fooをimportしたとき、C++コンパイラーはモジュール名fooに相当するモジュールバイナリファイルを探し出して使う。もしモジュールバイナリファイルがない場合、モジュールソースファイルを探してモジュールバイナリファイルを生成する。要するに今のビルドシステムがやっている依存関係の解決をC++コンパイラーが内部で直接やるようになる。ここでもやはり、モジュール名とモジュールソースファイルのマッピングが必要になる。

このモジュール名とファイル名のマッピングは、現状のままでは実装ごとに異なることになり、ポータビリティのかけらも存在しない使いづらいものになる。

モジュール機能を提供している他のプログラミング言語の例を列挙して比較してみるが、そもそも多くのプログラミング言語は単一の実行環境、単一の実装といったC++とは異なる状況にあるので参考にできないものも多い。

この文書では、モジュール名の文法を識別子から文字列リテラルにしてファイル名とマッピングするルールを追加するように提案している。

[PDF] P0779R0: Proposing operator try() (with added native C++ macro functions!)

なかなか野心的な提案。

expected<T,E>を返す関数が内部でexpected<U,E>を返す関数を呼び、エラーを返した場合はそのエラーを含むoptionalを伝播して返す処理を考える。

template < typename T >
using expected = std::expected<T, std::error_code > ;

expected<int> get_int() noexcept ;

expected<float> get_float() noexcept
{
    auto r = get_int() ;

    // 結果がエラーならば伝播する
    if ( !r )
        return unexpected( r.error() ) ;

    // floatがintを完全に表現できなければエラーを返す
    auto f = float( *r ) ;

    if ( int(f) != *r )
        return unexpected( std::errc::result_out_of_range ) ;
    else
        return f ;
}

このとき、エラーを伝播させるのがとてもだるい。いちいちエラーかどうか調べてエラーならエラーを返す処理を書かなければならない。

このボイラープレートコードを回避するためにCプリプロセッサーマクロを書くのも醜悪だ。

このコードは、コルーチン提案をまねてoperator tryをオーバーロードできるようにすれば解決できる。

template <class T, class E>
constexpr auto operator try(std::expected<T, E> v) noexcept
{
    struct tryer
    {
        std::expected<T,E> v ;

        constexpr bool try_return_immediately() const noexcept { return !v.has_value() ; }
        constexpr bool try_return_value() { return std::move(v).error() ; }
        constexpr try_value() { return std::move(v).value() ; }
    } ;
    return tryer{ std::move(v) } ;
}

これさえあれば、

auto r = get_int() ;
if ( ! r )
    return unexpected( r.error() ) ;
auto i = int(*r) ;

は、以下のように書ける。

int i = try get_int() ;

しかし、operator tryも冗長なコードを大量に書かなければならない。そもそもコルーチン提案自体が冗長なコードを多数書かなければならない。この問題は、ネイティブ言語マクロを導入すれば解決できる。

結局問題は、関数の呼び出し元の文脈でreturnしたいので、Cぷりプロセッサーに変わるネイティブなマクロがあれば、この問題は解決できるし、コルーチンが常用な問題も解決できるし、range-based forも実装できるし、割とあらゆるものがマクロで実装できることになる。

興味深いのはその提案している文法で、文字#を使っている。#はCプリプロセッサーにかけたあとのC++のソースファイルに残らない文字で、既存の実装はプリプロセス後に文字#があるとエラーを吐く。なので安全に機能拡張に使うことができる。

夢が広がる話だ。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2017-11-21

江添ボドゲ会 11月26日

以下の通り11月26日に自宅でボードゲーム会を開催します。

江添ボドゲ会@11月26日 - connpass

2017-11-20

C++標準化委員会の文書: P0735P0-P0769R0

P0735R0: P0735R0: Interaction of memory_order_consume with release sequences

memory_order_consumeがARMのとても弱いメモリーモデルでは実用的にならないのでなんとかしたいという文書。

P0737R0: P0737r0 : Execution Context of Execution Agents

現在のExecutor提案では実行媒体とかexecutorとか実行リソースについては考慮しているが、具体的なexecution contextについて考慮していない。とりあえず最低限の具体例を定める提案。

P0750R0: p0750r0: Consume

memory_order_consumeは未だにどのC++コンパイラーも実装していない。これは、C++のソースコードレベルでの依存と、ハードウェアでの依存に違いがあるためと、この業界でconsumeという用語の意味が一貫していないためだ。consumeを実装可能にするために色々と提案している文書。

P0752R0: std::vector Destruction Order

vectorの要素の破棄順序を自然にする提案。今まで規格でvectorの要素の破棄順序を規定していなかったので、実装ごとに差異があった。C++では破棄は構築の逆順に行われることが自然なので、vectorも配列やstd::arrayと同じく、末尾の要素から破棄される。

[PDF] P0753R1: Manipulators for C++ Synchronized Buffered Ostream

P0763R0のtypoを修正した。内容は同期バッファーでフラッシュをするかどうかを設定するマニピュレーターの提案

P0756R0: Lambda syntax should be more liberal in what it accepts

lambda-captureの制限緩和の提案。

現在、lambda-captureとして[&x, =]は誤りだ。正しくは[=, &x]。デフォルトキャプチャーの前に個別のキャプチャーを書くことはできない。

現在、lambda-captureとして[=, x]は誤りだ。正しくは[=]。デフォルトキャプチャーと同じ意味の個別のキャプチャーを書くことはできない。

しかし、これの意味は完全に曖昧製無く明らかであるし、初心者がハマる落とし穴であるので、制限緩和すべきであるという提案。

正しい。

P0757R0: regex_iterator should be iterable

filesystemのdirectory_iteratorはrangeとしても使える。

regex_iteratorもrangeとして振る舞うべきだという提案。

確かに、デフォルトコンストラクターが終端イテレーターになるタイプのイテレーターであれば、rangeにもなる。

[PDF] P0761R1: Executors Design Document

実行媒体を表現するExecutorについて。

[PDF] P0762R0: Concerns about expected<T, E> from the Boost.Outcome peer review

Boostのuncheckedの作者からexpectedに物申す文書。どうも些細な違いに思える。

P0766R1: Fixing small-ish functionality gaps in constraints

lambda式でrequires-clauseを使えるようにする提案。

P0767R0: P0767R0: Expunge POD

PODをdeprecated扱いにする提案。これまで規格でPOD型としていた部分はトリビアル型とし、初期化の文脈で「非POD型のグローバルオブジェクト」としていたところは「ユーザー提供コンストラクターで初期化されるグローバルオブジェクト」と書き直す。is_pod traitsはdeprecated扱いになりAnnex Dに移動する。

[PDF] P0768R0: Library Support for the Spaceship (Comparison) Operaton

operator <=>の標準ライブラリによる対応について。

operator <=>はこの前の会議で入ることがほぼ決定した等価比較と大小比較をいっぺんにできる演算子だ。

a == bのとき、a <=> bは0を返す。a < bのとき、a <=> bは<0を返す。a > bのときa <=> bは>0を返す。

また、戻り値は単なる整数型ではなく特別なクラス型で、大小比較できる場合はstd::strong_order, std::weak_order, std::partial_order、等価比較しかできない場合はstd::strong_equality, std::weak_equalityを返す。これらの型は派生関係でis-a関係を表現しているので、例えばstd::strong_orderはstd::weak_orderとしても使えるが逆はない。

大小比較が常にできてa == bのとき、常にf(a) == f(b)が成り立つのがstrong oder、成り立たないのがweak order

例えば文字列のcase insensitiveな比較は等しいと判断されても大文字小文字の違いがあるのでweak_order。

比較ができない値が存在する場合、partial order。

例えばIEEE-754の浮動小数点数はNaN, +0, -0があるのでpartial order。

この文書ではstd::strong_orderなどの標準ライブラリについて細かく定めている。ヘッダーファイルは<cmp>

[PDF] P0769R0: Add shift to <algorithm>

algorithmにshift_leftとshift_rightを追加する提案。

moveとmove_backwardと違い、同じレンジ内で操作する。rotateとは違う。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

glibcのpowがslowpowを呼んで遅い件について

Slow power computation by 64-bit glibc

x86-64 GNU/Linux環境でのglibcのpowは1.0に近い値を渡すと、768bitのソフトウェア実装された浮動小数点数計算を行い高精度な値を得ようとする。slowpowと命名されているこの実行パスは名前通りとても遅い。

glibcはとても基本的なライブラリなので、例えばRubyとかRustのようなプログラミング言語もこの問題に引っかかる。

手元で試したところ、GCC 7.2, glibc 2.26では、-Oと-ffastmathを指定するとpowにslowpowが呼ばれず高速に計算されるようだ。

これでもだいぶ高速化されたのだとか。

Improving math performance in glibc - RHD Blog

2017-11-13

C++標準化委員会の文書: P0650R1-P0722R1

[PDF] P0650R1: C++ Monadic interface

C++でモナドをやるためのライブラリの提案。どうもライブラリでやると煩雑になる気がするのだが。

[PDF] P0655R0: visit<R>: Explicit Return Type for visit

戻り値の型Rを明示的に指定できるvisit<R>の提案。

[PDF] P0657R1: Deprecate Certain Declarations in the Global Namespace

C言語における標準ヘッダーファイル<name.h>はC++では<cname>になった。C++形式のヘッダーファイルをincludeすると、std名前空間スコープに名前が宣言される。同時に、グローバル名前空間に名前が宣言されるかもしれない。

この提案は、C++形式のヘッダーファイルをincludeすると、グローバル名前空間に名前が宣言されることをdeprecated扱いする提案だ。なので、文面は「グローバル名前空間に名前が宣言されるかもしれない」から、「deprecated扱いではあるが、グローバル名前空間に名前が宣言されるかもしれない」になる。

P0658R1: Proposal for adding alias declarations to concepts

conceptのrequires式の本体にエイリアス宣言を書けるようにする提案。

template < typename T>
concept bool SomeReq = requires {
    typename value_type_t<T> ;
    requires Constructible<T, value_type_t<T> > ;
} ;

が、

template < typename T>
concept bool SomeReq = requires {
    using value_type = value_type_t<T> ;
    requires Constructible<T, value_type > ;
} ;

になる。conceptでネストされた型名記述に誤解を生みそうな気がする。conceptはすでにドラフト入りしているのだが、果たして普通のC++プログラマーが書けるようになるのだろうか。

P0670R1: Static reflection of functions

関数呼び出し式から呼び出される関数を得たり引数の型をそれぞれ得たりできるわりと軽めのリフレクション機能の提案。

void func(int);
void func(std::string);
using func_call_m = reflexpr(func(123));
using func_m = get_callable_t<func_call_m>; // reflects void func(int)
using param0_m = get_element_t<0, get_parameters_t<func_m>>;
cout << get_name_v<get_type_t<param0_m>> << '\n'; // prints "int"

[PDF] P0684R1: C++ Stability, Velocity, and Deployment Plans

C++標準化委員会が新機能を追加する際に下位互換性を破壊する変更を伴う時、ユーザーが既存のコードを変更する際に行われる行動を想定した上で、どのようにすべきかという提案。

C++の新しい規格を受容する時は以下のようなステップで行われる。

  1. 新しい規格をサポートした新しいコンパイラーにアップグレードする
  2. 新しいコンパイラーの古い規格モードでの既存のコードをコンパイルすると互換性の問題になる箇所について警告を受ける
  3. 警告を確認してコードを修正する
  4. 新しい規格モードを有効にする

これに従い、下位互換性を破壊する言語機能は容易に警告を出せるかどうかについて注意を払うべきである。

P0692R0: Access Checking on Specializations

知らなかった。

クラスのprivateなメンバーは当然クラス外部からアクセスできないわけだが、既存のほとんどのコンパイラーはテンプレートの明示的特殊化でアクセス指定を無視する。また、GCCとMSVCは部分的特殊化でもアクセス指定を無視する。


class A {
template < typename T >
class impl { } ;
} ;

template < typename T >
struct traits { } 

// 明示的特殊化
// 規格上はエラー
// GCC, Clang, ICC, MSVCは通す
template < >
struct traits< A::impl > { } ;

// 部分的特殊化
// 規格上はエラー
// GCC, MSVCは通す
// Clang, ICCはエラー
template < typename T >
struct traits< A::impl > { } ;

これを踏まえると、現状のC++実装がこの場合にアクセス指定を無視するよう実装していて、またこの仕様ではないとprivateメンバーの型に対する特殊化が出来ないことも考えると、規格を変更してこの制限を取り払ったほうがよいので、明示的特殊化と部分的特殊化ではアクセス指定の制限を緩和をする提案。

P0701R1: p0701r1: Back to the std2::future Part II

名前がよい。

内容はfutureに対する機能追加に対する提案。

[PDF] P0707R2: Metaclasses: Generative C++

現在提案されている静的リフレクションライブラリが入ったという前提でその静的リフレクション機能を使って実装できるmetaclassライブラリの提案。

constexpr {       // コンパイル時実行
    for ( auto m : $T.variables() ) // T型のメンバー変数それぞれに対し
        if ( m.name() == "foo" )  // "foo"という名前のメンバーがあれば
            -> { int bar ; }  // int型の"bar"という名前のメンバーを注入する。

}

割と衝撃的なコードだ。

P0722R1: Efficient sized delete for variable sized classes

sized deallocate functionにクラスへのポインターを取るオーバーロードを追加する提案。

クラスオブジェクトの近隣のストレージ領域を活用した動的にオブジェクトサイズを変えるクラスを実装する時、sizeを効率的に指定するために使える。


void operator delete ( X *, std::destroying_delete_t, その他の引数 ) ;

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

今回C++ドラフトに入った機能

Trip report: Fall ISO C++ standards meeting (Albuquerque) | Sutter’s Mill

Herb Sutterが今回の標準化委員会の会議でドラフト入りした機能についていち早く速報を出している。具体的な文書などはリンク先で確認してもらうとして、概要だけ説明する。

range-based forに初期化子が書けるようになった。

for ( auto result = f() ; auto && value : result ) ;

<bit>にbit_castingが追加された。

short from = 42 ;
auto to = bit_cast<std::uint16_t>(from) ;

operator <=>が追加された。

operator <=>は2つのオペランドの大小比較と等号比較を一度にできる演算子だ。この演算子を定義しておけば、残りの比較演算子はコンパイラーが自動的に生成してくれる。型システムによってstrong order, weak order, partial orderのいずれに対応しているかも切り替えられる。

atomic<shared_ptr<T>>が追加された。shared_ptrをアトミックに操作できる。

remove_cvref traitsが追加された。CV修飾子とリファレンスを消したいが、配列から要素型、関数から関数へのポインター型への変換は行いたくない場合に、decayの代わりに使える。

[[nodiscard]]が一部の標準ライブラリで使われることになった。

同期バッファー付きのostreamラッパーライブラリ、osyncstreamが追加。

// 複数のスレッドから同時に実行されうる関数
void f()
{
    std::osyncstream out( std::cout ) ;
    out << "hello, world!\n" ; 
}

スレッドセーフなostreamラッパーとして使うことができる。

<alogorithm>と<utility>の一部をconstexpr対応。

<comlex>をconstexpr対応。

atomic<floating_point_type>

stringとstring_viewにstart_with/end_withの追加。

また、現在策定中のconstexpr対応のnew, vector, stringは、後数回の会議でドラフト入りできる見込みだというぐらい本格的に議論されているようだ。本気で入れる様子が伺える。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

国家政府用のbitcoin破壊工作ガイド

How to Destroy Bitcoin with 51% (pocket guide for governments)

あなた、政府系の人ですか? bitcoin危険だと思いますか? いやいや滅相もない他にやるべきことがたくさんありますよ。まあでも、ちょっとここではbitcoinが疎ましいと思ってくださいよ。そうそう、あなたは中国だとします。

どうやって止めるのですか? みんな暗号通貨はセキュアで無敵だって言うじゃないですか。ちゃんとコントロールしたいあなた達政府系の人にとってはだいぶおつらいですよね。

Bitcoinを止めるのは実際難しくて、個人を何人か逮捕してサーバーを停止させたぐらいじゃ止まりません。Silk RoadやE-GoldやLiberty Reserveとは違うんですよ。そして参加者はだいたい権力を嫌ってます。

暗号通貨では停止させるべき中央サーバーってものがないんですね。ユーザーはみんな、フルノードをセキュリティ目的で実行してますから(理想的な世界ならばそうなのですが、現実ではだいたいのユーザーは有名所のクラウド提供されたWebウォレットを使ってるようですね)

でも、マイナーとかバリデーターとかウィットネスとか呼ばれているルールに従って選ばれた調停者ってのがいます。Bitcoinではマイナーはハッシュ(計算コストが高く安い電気が必要)の発見に精を出しています。そしてほとんどのマイナーはあなたの国にいるんですね。

画像:中国が世界の71%のマイナーを持つ。
https://cdn-images-1.medium.com/max/1600/1*RyaqdlQMBhrdxDlpnCh7eQ.png

マイナーを止めるってのはバカげた話です。Bitcoinはその維持にマイナーを必要となんかしていません。マイナーはセキュリティのために必要なのです。なので、あなたがた政府系の人がマイナーを差し止めたら、難易度調整が入って他の国のマイナー達がネットワークをセキュアにし続けるってわけです。

Bitcoinを殺すなら、ガツンと凶悪な51%攻撃ってやつをやってやりましょう。

ステップ1、bitcoinを買い集める

大手の交換所すべてに、アカウントを作成しましょう。取引所ひとつにつき10から40アカウントぐらい。さて100箇所ぐらいの交換所にそれぞれ40個のアカウントを作りましたね。上出来です。まず景気づけに1億ドル分ぐらいのBitcoinを買いましょう。あなた、政府系の人ですよね? 国家の安全のためにはそれぐらい捻出できますよね?

これを書いている時点でBitcoinの市場総量というのは950億ドルぐらいなので、0.1%ぐらいの量になります。わりかし悪くないレバレッジですね。

交換所にある通貨をすべてあなたの所有するウォレットに送金しましょう。個々が一番難しいところで、暗号通貨のウォレットのユーザーエクスペリエンスは最悪で、誤操作で結構な金を失う可能性があります(ジョークON)

ステップ2,51%のマイナーを見つけましょう

https://www.buybitcoinworldwide.com/mining/china/によれば、世界のマイナーの71%は中国にいます。彼らは地下に隠れていますが、まあでも、そこはあなた中国政府。あなたは人民のスマフォにスパイウェアの導入を義務付けたり、https://thenextweb.com/asia/2017/07/25/chinas-forcing-its-citizens-to-install-a-terrifying-big-brother-app-on-their-phones-or-go-to-jail/しているわけですから、捜索はそう難しくはないでしょう。

ヒント:彼らは盗電してます。調べましょう。

たとえばこれはオルドスの例ですね(北京からそう遠くないですよ)

https://qz.com/1055126/photos-china-has-one-of-worlds-largest-bitcoin-mines/

ステップ3、秘密作戦

さて、秘密警察組織に作戦実行のデイXまでにできるだけ多くのマイナーを捜索するよう命じましょう。

これは法執行機関さんのやることですので、あなたの国のお巡りさんは説得に当たってマイナーの頭に銃を突きつけてもよいでしょう。このお願い方法はマイナーが知らんふりを決め込んでいるのを説得させるのにとても効果的です。

国家政府が物事を成し遂げるってのはこういうことですね。しかも、あなたは中国なのですから、こういう活動をするにあたってリベラルを装う必要すらないのですよ。

ステップ4、倍々ゲーム

マイナー達に以下の操作を行わせましょう。あなたのウォレットの単一のブロックから4000トランザクション(1つあたり25000ドル)ほど掘らせて全体に行き渡らせます。この4000トランザクションはステップ1で作ったアカウント群に送金を戻すことに使います。

通常、取引の成立には6ブロック重ねる(承認)ことが必要です。

さて、世界のネットワークは今や、あなたのブロックの上にブロックを重ねています。その間に、あなたのほったブロックの上に自分でブロックを重ねましょう。

つまり、ブロックNに対してみんなにブロックN+1を送ります。するとみんなBlockN+2とBlockN+3を掘るのに勤しむわけです。あなたはかわりに、別ブランチのブロックN+1BとブロックN+2Bを掘りましょう。

あなたは51%を持っているのですから、たぶんみんなよりは速く掘れるでしょう。よそのマイナー達があなたのブロックに対して6ブロック承認を終えたら、あなたは持っているBitcoinを全部他の何かに交換しましょう。LitecoinだとかRippleだとかEthereumとか、まあ何でもいいです。普通に交換できるはずです。そしたら、あなたは交換したaltcoinをあなたのウォレットに送ります。

BTCをすべてaltcoinに変えて、altcoinを引き出した後に、あなたの4000トランザクションを無効化するもう一つの秘密のブランチを公開します。

これはリオーガニゼーションというやつです。これでネットワークはあなたのチェインを本流として受け入れ、オーファン化した他のトランザクションのことは忘れなければなりません。

ステップ5、暗号通貨市場の価値が暴落するのを眺めましょう

さて、これによって1億ドルの価値があるBitcoin(交換所はもう持っていない)が、1億ドルの価値があるaltcoin(あなたが持っている)に変わったわけです。おめでとうございます。あなたは2億ドル分の暗号通貨を手に入れました。その市場価値は暴落するでしょうが、目的は市場価値の暴落にあるのだから問題ないでしょう。

この攻撃をもう一発行ってもいいのですが、その必要はないでしょう。この規模の攻撃が一度起こっただけでも、暗号通貨に対する信頼に大打撃となり、数年は市場が暴落するでしょう。

よその国家にはどうしようもできません。Bitcoin自体にはまともな法規制や慣習が存在しないのですから。なので秘密に攻撃してもいいし、公に攻撃しても構いません。

再考

この記事は筆者の現状に対する不満を述べたものであるが、筆者はBitcoinに反対するものではない。筆者は我々が現在陥っている愚行について憤っているのだ。

我々はもう一度原点に立ち返ってパラノイアになるべきであり、我々の脅威モデルを適切に設定すべきなのだ。お前が今やっているのは何だ? スマートコントラクト? ブロックチェインによる個人認証? クソコインとクソコインを分散取引? ICO? そういうことは検閲耐性というブロックチェインにおける唯一の重要な事に比べれば完全に無価値なものだ。検閲耐性以外の余りのクソはお前の5ドルのサーバーでも1000倍は効率的にやってくれる。

お前が検閲耐性などいらないのであれば、わざわざビットコインなど使わないでもっと効率的にやれ。

この攻撃はEthereumに対してはより簡単だ(ステップ1,Ethereum創始者のVitalikを捕まえろ)。他の暗号通貨に対しては51%攻撃というのはヘリコプター一台分の金を使ってASICを投下するだけでよい。なのでやるのであれば、Bitcoinに対して攻撃して他の暗号通貨がドミノ効果で信用を失っていくさまを眺めるがよい。

追記:読者が中国ではなく、中国にジェイムズ・ボンド級のスパイを放って51%のマイナーをハイジャック出来ない場合、Bitcoinに対して51%攻撃をしかけるのはもっと難しい。なので中国を説得してやってもらうしかない。

ちなみに、この51%攻撃(Double Spending)は全体の51%のハッシュレートを持っていれば確実に成功するが、51%より少ないハッシュレートしか持たない場合でも、確率的に成功する。この攻撃は一度成功させるだけで信頼に大打撃を与えられるので、全体の20%程度のハッシュレートしか持たず、確率的に数%しか成功しない場合でも、中国共産党が本気で暗号通貨を潰しにかかった場合、割と実現可能性の高い攻撃ではある。

Analysis of hashrate-based double-spending

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2017-11-10

国際標準規格の紹介 〜ISO国際標準規格プログラミング言語C++について〜

はじめに

こんにちは、C++標準会員の江添です。

今日は普段から表に出ている、閉鎖的なゲーム開発では使われていない開発環境についての紹介をします。

今回紹介するのは、ISO国際標準規格プログラミング言語の「C++」です。

なんと表の世界では、国際標準のプログラミング言語は、コンパイラーベンダーやC++利用企業と行ったC++に利害関係を持つ多数の代表が国際標準規格を制定しているのです。当たり前でしょ?

C++は以下のような特徴を持っています。

  • エラーに気づきやすい静的型付け言語
  • C++経験者が理解しやすい言語仕様
  • OSに依存しない移植性の高いC++実装
  • 実行環境に依存しないC++規格準拠の挙動の定義
  • C/C++の関数・クラスに最初からバインドされている

今でもクローズな実装の詳細のわからないプログラミング言語がたくさんあり、それらを使うデメリットは数多くあります。

国際規格の言語を使うのは様々な利点があるからです。

一番大きなものは「権力者の慈悲にすがるしかない状況が発生しない」ことです。もし特定の個人や団体が言語を所有している場合、将来の変更が既存のコードを壊さないこと、自分のほしい新機能が追加されることは、権力者の慈悲にすがるしかありません。ISO傘下の国際規格として制定されることで、企業や個人は等しく最適な言語仕様を考えることができます。

今回はその中の一つとしてCRTPという技法を紹介します。

CRTP

自分の型を返す関数ってよくありますが、面倒くさいですよね。

class A_Class
{
public :
    static auto Create()
    {
        return std::make_unique<A_CLass>() ;
    }
} ;

CRTPを使うと、先程のコードを以下のように書くことができます

template < typename T >
struct factory_mixin
{
    static auto Create()
    {
        return std::make_unique<T>() ;
    }
} ;

class A_Class
    : public factory_mixin<A_Class>
{ } ;

もう少し複雑な例を挙げます。これと似たような(しかもコピーする必要がある)クラスを、名前を変えながら数十、数百と増やしていくことを想像してみてください。

class A_VeryLongClassNameMadeByEzoe
{
public :
    static auto Create()
    {
        return std::make_unique<A_VeryLongClassNameMadeByEzoe>() ;
    }

    static auto CreateWithTwo()
    {
        return std::make_unique<A_VeryLongClassNameMadeByEzoe>(2) ;
    }

    static auto Create( int value )
    {
        return std::make_unique<A_VeryLongClassNameMadeByEzoe>(value) ;
    }

    A_VeryLongClassNameMadeByEzoe() { }
    A_VeryLongClassNameMadeByEzoe( int value )
        : value(value) { }
private :
    int value = 0 ;
} ;

CRTP実装は簡単ですね。

template < typename T >
struct factory_mixin
{
    template < typename ... Types >
    static auto Create( Types && ... args )
    {
        return std::make_unique<T>( std::forward<Types>(args)...) ;
    }

    static auto CreateWithTwo( )
    {
        // T::T(int)を持たない型は除外
        constexpr bool b = requires { T(2) ; } ;
        static_assert( b, "Type doesn't have constrcutor T::T(int)." ) ;
        if constexpr ( b )
        {
            return Create(2) ;
        }
    }
} ;

CRTPがあるとゲームでよく使われる状態遷移モデルのような仕組みを描くのが大変楽になります。

現場では以下のようなクラスがたくさん作られるため、面倒なコードの重複によるバグを減らす(これが大切)ことができるのです。


template < typename T >
struct change_state_mixin
{
    static void ChangeState( Obj aObj )
    {
        T newState( aObj )
        aObj.stateChanger().setNextStage(newState) ;
    }
} ;

// 待機状態
struct StateWait
    : change_state_mixin<StateWait>
{
    void procAnim()
    {
        // 状態ごとに違う処理
    }
}

// プログラマーにコピペを強いる状態
struct StateForceHallabProgrammerToCopyPasteTheBoilarPlateCode
    : change_state_mixin<StateForceHallabProgrammerToCopyPasteTheBoilarPlateCode>
{

    void procAnim()
    {
        // コピペ処理
        yank() ;
        jjjjjjjjjjjjjjjjjjjjjjj() ;
        paste() ;
        enter_insert_mode() ;
    }
}

まとめ

わざわざ閉鎖的な環境で独自言語を開発しなくても、ライブラリ作者向けの強力な機能が提供されているのがC++の強みです。時代とともに変化する様々な環境にも、C++は対応し続けています。

これ以外にも、C++ではボイラープレートコードを削減するために様々な機能がありますが、それはまたの機会にぜひ。

国際標準規格C++は、C++に利害関係を持つ様々な個人や企業がC++標準化委員会に代表を送り、ISOのルールに則って新機能の考案や策定を進めています。もし現行のC++に不満があるのであれば、C++標準化委員会に代表を送って提案し、実験的実装をし、その価値を示し、国際会議で議論して、規格策定に貢献しましょう。

真面目なまとめ

元ネタは以下の通り。

社内開発環境の紹介 ~社内製プログラミング言語Mintについて~ | ハル研ブログ | ハル研究所

元ネタで対処しようとしている問題は、コードをコピペするから生じるのであって、コードをコピペしなければよい。コードの重複を防ぐためには、昔から基本クラスという便利な機能がある。これにC++のテンプレートを組み合わせればボイラープレートコードをコピペして一部変更という不毛な処理を人間がする必要はない。そして、コンパイル時に解決できる問題はコンパイル時メタプログラミングで解決できる。

そして、C++の言語機能に不満があるのであればC++標準化委員会に出てきて議論するべきだ。例えばC++17にはメモリ解放処理で何もしない、単なるポインターを加算して返すだけのメモリ確保ライブラリのモノトニックバッファーリソースが入った。C++20には、ソート済みの連続したストレージで実装された連想コンテナーのflat_map/flat_setや、歯抜けを許す連続したストレージで実装したvectorのようだが中間要素の削除が早いcolonyなど、コルーチン、スタックフルコンテキストスイッチライブラリなど、ゲーム用途にも便利な新機能がたくさん提案されている。他にも、this_typeで解決したい問題にも適用できる、静的リフレクション機能も活発に議論されている。C++を活用するのであればC++に貢献すべきだ。

2017-11-08

C++標準化委員会の文書: P0586R0-P0649R0

P0586R0: Safe integral comparisons

2つの整数型の値を安全に比較するためのライブラリの提案。

2つの異なる整数型の値を比較する時、符号が違ったり、一方がもう一方の表現できる範囲ではない場合、人間にとって不自然な結果になる。安全に比較するためには様々な符号や暗黙の型変換の考慮が必要だが、それを正しく書くのは面倒だ。なので標準ライブラリがほしい。

P0593R1: p0593r1: Implicit creation of objects for low-level object manipulation

以下の現行規格では未定義の挙動となるコードの挙動を規格で定義する提案。

struct X { int x ; } ;

int main()
{
    X * p = (X *) malloc( sizeof(X) ) ;

    p->x = 0 ; // 未定義の挙動

    free( p ) ;
}

このようなコードはC言語ではよく書かれるが、C++では未定義だ。構築されていないオブジェクトへのアクセスは未定義の挙動だ。このコードはオブジェクトを構築していない。したがって挙動は未定義だ。

しかし、現実を見ると、プログラマーはこのコードが動くことを期待しているし、現実のC++コンパイラーはこのコードの挙動について一致している。規格は現実を追認すべきだ。

すると、どのような型ならば制限を緩和するかということになるが、構築や破棄にあたってコードが実行されない型ならばよい。これには、スカラー型、配列型、トリビアルなコンストラクターとデストラクターを持つクラス型が当てはまる。

[PDF] P0609R1: Attributes for Structured Bindings

構造化束縛に属性を記述できる文法を追加する提案。

auto [a, b [[attribute]], c ] = e ;

[PDF] P0624R1: Default constructible stateless lambdas

stateless lambdaによって生成されるクロージャーオブジェクトをデフォルト構築可能にする提案。

auto f = (){} ;
decltype(f) g ; // OK
g() ; // OK

stateless lambdaならばクロージャーオブジェクトをデフォルト構築しても問題になることはない。クロージャーオブジェクトがデフォルト構築可能になることで、より汎用的に関数オブジェクトとして使いまわせる。

[PDF] P0631R1: Math Constants

数学定数を追加する提案。

ヘッダーファイルは既存のヘッダーファイルに追加するのであれば<cmath>か<numeric>、新しいヘッダーファイルにするのであれば<math_constants>が提案されている。

さて、数学定数はstd名前空間スコープの下に宣言されるわけだが、これは問題がある。というのも数学定数にはeといった小文字一文字の名前もあるので、以下のようなコードは壊れる。

using namespace std ;
int e ;

これは自業自得と言えば自業自得だが、とはいえ小文字一文字という名前はstd名前空間下であっても危険すぎる。

そのため、e_vのような形にするか、あるいはstd::math_constants下に置くという提案がされている。

提案では数学定数は変数テンプレートの形を取る。

template < typename T > inline constexpr T e ;

これにより任意の型に対応できる。

P0634R1: Down with typename!

依存名が型を意味するときにはtypenameを書かなければならないが、文脈上型しか書けない場所には書かなくてもすむようにする提案。

template < typename T >
auto f( T::type ) -> T::type
{
    using t = T::type ;
    new T::type ;
    static_cast<T::type *>(nullptr) ;
}

template < typename T >
struct A : T::type
{
    T::type t ;
} ;

現在の標準規格では以下のように書かなければならない。


template < typename T >
auto f( typename T::type ) -> typename T::type
{
    using t = typename T::type ;
    new typename T::type ;
    static_cast<typename T::type *>(nullptr) ;
}

template < typename T >
struct A : T::type
{
    typename T::type t ;
} ;

大変だ。

P0636R2: Changes between C++14 and C++17

C++14からC++17の間の変更点。

[PDF] P0642R1: Structural Support for C++ Concurrency

構造化プログラミングがループをパターン化して簡単に書けるようにしたように、並列処理をパターン化して簡単に書けるようにした並列処理に対する構造化サポートのためのライブラリの提案。

P0644R1: Forward without forward

forwardを書かずにforwardする提案。

Perfect Forwardingはだるい。例えば関数オブジェクトFuncに引数Argsを渡して戻り値を返す転送関数をPerfect Forwardingで書く場合

template < typename Func, typename ... Args >
decltype(auto) f( Func && func, Args && ... args )
{
    return std::forward<Func>(func)( std::forward<Args>(args)... ) ;
}

これをラムダ式で書く場合

auto f = []( auto && func, auto && ... args ) -> decltype(auto)
{
    return std::forward<decltype(func)>(func)( std::forward<decltype(args)>(args)... ) ;
}

とてもだるい。

そこで、この提案ではオーバーロード不可能な単項operator >>を追加することにより、適切なforwardingを行う昨日を提案する。

template < typename Func, typename ... Args >
decltype(auto) f( Func && func, Args && ... args )
{
    return (>>func)( >>args... ) ;
}

auto g = []( auto && func, auto && ... args ) -> decltype(auto)
{
    return (>>func)( >>args... ) ;
}

だいぶ楽になった。

[PDF] P0649R0: Other Product-Type algorithms

構造化束縛で分解できる型をproduct typeと呼び、その分解して得られる値を得るためのライブラリがP0327R2で提案されている。この提案は、product typeに対するtuple風アルゴリズムの提供だ。

たとえばfor_each

int main()
{
    std::tuple t(1, 3.14, "hello") ;

    std::product_type::for_each( [](auto x){ std::cout << x << '\n' ; }, t ) ;
}

構造化束縛できるあらゆるproduct typeに使えるfor_eachだ。他にも様々なアルゴリズムが提案されている。Boost.FusionやBoost.Hanaからアイディアを得ている。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2017-11-06

C++標準化委員会の文書: P0533R1-P0573r2

[PDF] P0533R1: constexpr for <cmath> and <cstdlib>

algorithmをconstexpr化する提案で分離されたcmathとcstdlibのconstexpr化についての提案。組版がクソで読む気がしないが、一般的な算術演算子だけで実装できるものはconstexpr化できる。errnoの書き換えはconstexprではできないが、constexprの文脈で評価されたときの挙動を規定すればconstexpr化できる。

[PDF] P0534R3:call/cc (call-with-current-continuation): A low-level API for stackful context switching

いわゆるcallccを実現するためのスタックフルなコンテキストスイッチを実現するための低級ライブラリの提案。コルーチン提案よりよっぽどわかりやすいのでこっちのほうがいい。

P0539R2: A Proposal to add wide_int Class

Nビットの整数型を表現するwide_int<N>の提案。

より大きな整数型の需要は年々高まるばかりで、GCCとClangには__int128型が存在する。将来的には256bitなどのより大きな整数型が出てくることは想像にかたくない。そこで、大きなサイズの整数型を汎用的に表現する方法がほしい。

この提案では2の乗数の整数型しか扱わないが、将来的には拡張できる余地を残す。

P0546R1: P0546r1 : Span - foundation for the future

配列に範囲外チェックを行いつつアクセスできるストレージを所有しないライブラリspanの提案。

[PDF] P0549R2: Adjuncts to std::hash

ある型Tが有効なstd::hash<T>を持つかどうか調べるis_enabled_hash<T>、CVリファレンス修飾子を取り除いた型が有効なハッシュを持つかどうか調べるhash_for<T>、そしてハッシュ値を得るhash_value<T>(T && t)、ハッシュが例外を投げないか返すis_nothrow_hashable<T>の提案。

[PDF] P0551R1: Thou Shalt Not Specialize std Function Templates!

標準ライブラリの関数テンプレートはユーザーが特殊化してはならないというルールの追加。なぜならば、ろくな結果にならないから。

P0561R2: An RAII Interface for Deferred Reclamation

リーダーライターロックのパターン、つまりあるオブジェクトの値を読むリーダーがリーダーロックをして値を読む。アップデーターは新しいメモリを確保して差し替えることで値を更新する。リーダーロックはアップデーターを妨げない。メモリの解放は最後のリーダーロックが外れて問題ないときに行う。

このようなパターンを実現する方法として、リファレンスカウント、RCU、ハザードポインターといった方法が考案されていて、実際標準ライブラリに入れるべく提案もされている。問題は、提案は個別の実装方法に特有の低級な操作を提供している。本提案は、このパターンを実現する実装方法によらない汎用的な高級APIを提案する。

たしかにほしい。

[PDF] P0566R3: Proposed Wording for Concurrent Data Structures: Hazard Pointer and Read-Copy-Update (RCU)

ハザードポインターとRCUの文面案。もともと別の提案だったが似通った機能を提供するため、文面案を一緒にすることにしたとのこと。

P0571R1: p0571r1: Type Requirements for <numeric> Algorithms

<numeric>の一部のアルゴリズムに型要件が曖昧な部分があったので修正する。例えばaccumulateなどのアルゴリズムで、内部で使われる一時オブジェクトの型がどうなるか曖昧で実装の差異が生じていた。

P0573R2: Abbreviated Lambdas for Fun and Profit

省略ラムダの提案。

ラムダ式の本体として=> 式と書けるようになる。=>の場合、戻り値の型推定は-> decltype(auto)扱いになり、 例外指定もnoexcept(式)扱いになる。=>の場合、引数の型を省略できる。その場合auto &&と書いた扱いになる。


std::sort( b, e, [](a,b) => a < b ) ;

このコードは、


std::sort( b, e, []( auto && a, auto && b ) noexcept(a < b) -> decltype(auto){ return a < b ; } ) ;

と書いたものと同じ意味になる。

ほしい。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2017-11-05

VimConf 2017に行ってきた

以下で告知されているVimConf 2017に行ってきた。

vim-jp » VimConf 2017を開催します

私はVimを毎日使っているし、実際このブログ記事もNeovimで書いているが、肝心のVim力はさほど高くない。私はHJKLではなくArrow Keyを使うし、マウスも使う。大抵のテキスト編集作業はInsert Modeで行う。ノーマルモードで行う作業はペースト(これも面倒なのでInsert Mode内でCtrl+Rを使うこともしばしばあるのだが)やgJ、数行の自作の関数によるHTMLでエスケープが必要な文字のエスケープとか、ブログ記事をBloggerで公開するときに本文やタイトルをコピーしたり、Bloggerへのリンクを挿入したりする作業だ。その他には:makeも使っているが、これは別にVimとはあまり関係がない。人から「でもあなたはoを使っているでしょう。他のエディターにはなかなか見かけない便利な機能です」と言われたことはあるし、実際oは使っているが、oはVimがモードを持つから存在するだけで、モードを持たないテキストエディターならばoはいらないのではないかと思う。

そんな私がなぜVimConf 2017に行ってきたかというと、たまたま私が雇用されている会社、ドワンゴがVimConf 2017のスポンサーになっていたため、スポンサー枠がひとつ余っていたのだ。

さて、会場の空気はと言うと、だいぶ常人離れしていて、毎朝Vimをビルドするのは当たり前という人がゴロゴロいた。

発表者と発表に使ったスライド資料の一覧を記したタイムテーブルがどこかにあればいいのだが、そういう便利なものが見当たらないので、仕方なく記憶を頼りに書く。

haya14busaの自分のVimに関する活動史についての発表、これは特に大した印象が残らなかった。

fatihのvim-goの発表。vim-goとはvimをgo開発環境にするプラグインだ。その機能はgoの実装をインストールする機能に始まり、テストでcoverageされた部分のコードをハイライトする機能まであり、なかなか良さそうだ。

デバッガーのサポートは現状のvimでは難しいが、LSPへの対応により状況は変わるだろうとの期待がある。

VimConf 2017では昼になかなか豪華なすき焼き弁当がでた。壇上にmattnとk_tanakaとkaoriyaが上がって弁当を食べるところを眺めながら弁当を食べた。

昼の休憩の間に色々と参加者に対する質問が行われたが、Microsoft Windowsを使っている参加者は数えるほどしかいなかった。WindowsdもMacOSdもGNU/LinuxでもないOSを使っているものはいるのだろうかと思ったが、Chrome OSを使っている者がいた。少し期待していた答えとは違う。Wayland上でvimを使っているのは、私ともうひとりしかいなかった。ただ、私ももうひとりも、Waylandは使っているものの、vim自体は端末エミュレーター上で動かしているため、果たしてそれはWaylandで使っていると言えるのか疑問だ。

cocoponのcolor schemeを作成する発表。color schemeを作るための割と全てが解説されていた。どうやらcocoponによると、我々プログラマーは一日のうち8時間は眠り、8時間を日常の雑事や余暇に使い、8時間は働いている。すなわち、我々は一日のうち8/24にあたる33%の時間はcolor schemeを見ていることになる。したがってcolor schemeは極めて重要だ。通勤途中に急にcolor schemeを眺めたくなることがあるらしいので、Vimのcolor schemeを比較できるWebサイトを作り、モバイル対応もさせているらしい。

t9mdによるAtomでVim風の操作を実現するVMPの発表。Vimの操作はテキストバッファーに対する変更であり、その文法はoperator + targetであるといったVim操作の本質の話、後半ではVim操作のデモ(Atomで動くVMPによるデモ)が行われた。

私はVimでtext objectというものは使っていなかったのだが、熟練のvimmerはyipとvipが息を吐くように行えるのだという。AtomのVMPではtext objectを入力すると対応する部分がフラッシュするようになっていて、UIフィードバックがあるので、操作しやすくなっている。これはVimにも欲しいところだが、AtomはElectronで実装されていれるがためにCSSを数行書くだけでできる実装が、Vimでは難しいとのことであった。

text objectは覚えたいところだ。

senopenはPOSIX原理主義者として移植性の高い.vimrcの記述方法についての発表。移植性の高い.vimrc、つまりどんな環境に持っていっても変更無く正しく動作する.vimrcはどう書けばいいのか。まず、現実の環境で対応しなければならないVimのバージョンの下限を調べる。どうやらある有名なクラウドサーバーではCentOS 5.5をサポートしているらしく、するとVim 7.0を下限とすればよい。また、WindowsとPOSIXではファイルパスのフォーマットが違う。文字コードが違うといった問題があり、これに正しく対応するための記述方法が示された。

暗黒美夢王ことShougoMatsuは暗黒の力によって作られているスニペットプラグインの発表。スニペットプラグインとは何か。スニペットプラグインの実装方法とその長短について。新しいdeoppet.nvimではVimにスニペットプラグインを作りやすくするための機能を追加することにより、パフォーマンスの問題を解決したなどと話していた。今回は歌はなかった。

dice_zuは、ordinaryな人間がVimに貢献する方法について発表。どうやら彼の定義するところのordinaryには、毎朝Vimをビルドすることが含まれるらしい。

p_ckはVimのシンタックスハイライトを記述する新しい方法について発表。既存のシンタックスハイライトの記述方法がいかにダメかという話をしていた。

lambdalisueはgitのVimフロントエンドであるgina.vimについて発表。この発表を聞いた我々は、スーパーVimマン2になれるそうだ。スーパーVimマン2とは、CLIツールをVimで代替するスーパーVimマンを超えた人のことだそうだ。ちなみに、スーパーVimマン2を超えたスーパーVimマン3が存在し、これはデスクトップアプリをVimで代替する超人のことだそうだ。肝心のgina.vimだが、git logをVimで表示することができ、かつコミットを選択するとその変更内容を別枠で表示するなどの機能があり、たしかに便利そうだとは思った。しかし私はgitにVimフロントエンドが必要だとは思わない。

VimConf 2017は国際カンファレンスという位置づけであり、発表の半分以上は英語で行われ、英語と日本語の同時通訳がついていた。私は英語は英語として聞いていたので日本語通訳は聞いていなかったのだが、どうも日本語通訳はだいぶ独創的な翻訳でなかなか面白かったらしい。何でもアレがソレしてヤバイとか。

VimConfの規模をもっと大きくして来年も行うために、スポンサーを募集しているそうだ。

2017-11-01

C++標準化委員会の文書: P0399R0-P0515R2

[PDF] P0399R0: Networking TS & Threadpools

ネットワークライブラリーはスレッドプールを使いたいがどのようになるのかということを大雑把に説明したスライド資料。

[PDF] P0424R1:Reconsidering literal operator templates for strings

文字列リテラルに対するユーザー定義演算子で文字列を(harT const *, std::size_t)で取れるようにする提案。

template < typename CharT, CharT const * str, std::size_t length >
auto operator "" _udl() ;

auto x = "abcd"_udl ;

[PDF] P0429R2:A Standard flat_map

ソート済みの連続したストレージで実装されるset/mapの実装。flat_set/flat_mapの提案。

既存のset/mapはノードベースのバイナリーツリーによる実装で、これは現代のメモリが凄まじく遅くキャッシュミスが悲惨なことになるアーキテクチャではとても遅い。連続したストレージはキャッシュが聞きやすく、キャッシュが聞いたメモリ領域への読み書きは高速に行える。すると、ソート済みの配列をバイナリサーチする実装のほうが、現実のアーキテクチャでは速度の点で有利だ。

入ってほしい。

P0443R3:A Unified Executors Proposal for C++

実行媒体を表現するexecutorライブラリの提案

P0447R4: Introduction of std::colony to the standard library

歯抜けを許す連続したストレージ上に構築される要素の順序保証のないコンテナー、colonyの提案。

vectorのような連続したストレージ上に構築される要素の順序保証のあるコンテナーを考える。

std::vector v = {1,2,3,4,5} ;
// 中間の要素を削除
v.erase( v.begin()+2 ) ;

このコードは遅い。なぜかというと要素3を削除したあとに、要素3が構築されていたメモリー上に要素4をコピーし、要素4の場所には要素5をコピーして、間を詰めなければならないからだ。vectorでは最後尾以外の要素の削除は遅い。

では要素を詰めなければいいのではないか。eraseを行った後のコンテナーの中身が、{1,2,無効,4,5}となるデータ構造はどうだろうか。要素にアクセスする際は無効になった要素を読み飛ばして無視する。要素2と4の間に要素を挿入すると、無効化されていたストレージに要素が追加される。さらに、位置を指定せずに挿入すると、無効化されているストレージのどこかに要素が追加される。

colonyは要素の順序を保証しないが、中間の要素が定数時間で行える特性を持つコンテナーだ。

なお、リファレンス実装では、無効要素の読み飛ばしは要素ごとに無効フラグを持つのでもなく、ビットマップで無効フラグを持つのでもなく、jump-counting skipfieldと呼ばれる手法を用いるらしい。これによって連続した無効要素を読み飛ばすのがキャッシュ効率よく行えるそうだ。

The Advanced “Jump-Counting” Skipfield Pattern

[PDF] P0461R2: Proposed RCU C++ API

同期処理のためのRCUライブラリの提案。

P0479R2: Attributes for Likely and Unlikely Statements

起こりうる可能性の高さと低さを指定する属性、[[likely]]と[[unlikely]]の提案。

例えば分岐時に実行される可能性の低いブランチに指定することで、コンパイラーの最適化を助けることができる。


if ( is_error )
[[ unlikely]] {
// めったに起こらないエラー
}

GCCとClangは独自拡張として__builtin_expectという同等機能のcompiler intrinsicsを提供している。MozillaやChromiumのような現実のコードでも使用されている。

現実にどのように活用されるかというと、例えばキャッシュを多用するアーキテクチャでは、コードブロックの配置の場所によってキャッシュされるかどうかが変わるので、分岐時により実行される可能性の高いコードをキャッシュされやすい領域に配置することができる。x86では、隣接する複数の命令は内部で単一の命令にまとめられたりするので都合が良い。

[PDF] P0506R2: use string_view for library function parameters instead of const string & / const char *

string_viewを標準ライブラリで文字列を受け取る引数の型として積極的に使っていこうという提案。std::string const &やchar const *で受け取るよりよい。

[PDF] P0514R2:Efficient waiting for concurrent programs

スレッド所有がないmutexとして、atomic_semaphoreの提案。atomic操作にatomic_waitやatomic_notify_oneも追加。そしてcondition_variable_atomicも提案。

[PDF] P0515R2:Consistent comparison

C++における比較をより厳密にするための新しい演算子、operator <=>の提案。

operator <=>(a, b)はa, bの大小関係と等号関係に応じて適切な3値のいずれかを返す。また、strong order, weak order, partial order, strong equality, weak equalityも型で表現する。

これでだいぶ比較がマシになる。今まで同じ演算子にごちゃまぜにしすぎていた。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0