altebute.hatenablog.com

犬も歩けば規格にあたる

機工城アレキサンダー:律動編 ドロップテーブル他

必要個数

種別 対価となるアイテム 必要個数
頭防具 ミダースレンズ 2
胴防具 ミダースシャフト 4
手防具 ミダースクラン 2
帯防具 ミダースチェーン 1
脚防具 ミダーススプリング 4
足防具 ミダースペダル 2
アクセサリ ミダースボルト 1

各層ドロップテーブル

1 * * *
2 * * *
3 * * *
4 * * *
  • 各層のドロップは3種類と過程( 起動編準拠 )
  • 起動編とドロップテーブルは同じっぽい
  • 4層では各プレイヤー確定で ミダースの歯車 がドロップ。週制限あり。

以下未整理メモ

各層で取得するアイテムはおそらく下記の感じ。 伝承装備は考慮していない。順不同。

  1. チェーン, ボルト, ペダル, ペダル, ボルト
  2. ボルト, ボルト, レンズ, レンズ
  3. スプリング, スプリング, スプリング, スプリング, クランク
  4. シャフト, シャフト, シャフト, シャフト, クランク

以下注釈

  • クエスト報酬でボルトは今回は無し
  • 残りのアクセ枠( 指 )は伝承で埋める。

同人活動の感想を送る上で最も妨げになるものは「面倒くささ」だと思う

TLでチラチラ話題になっていたので前々から思っていた事をアウトプット。

感想が貰えないのは、作品がつまらなかったりする場合よりも、 感想を送る行為そのものの面倒さに起因する事が多いように思う。 *1

連々と列挙すると、

  • 作者のウェブサイトを探すのが面倒くさい
  • 感想の投稿フォームを探すのが面倒くさい
  • 感想がメールだったりするとなんかやだ
  • Twitterで感想を送るのは双方向だからなんかやだ
  • 投稿フォームがあってもメールアドレス必須だと面倒くさい
  • 投稿フォームがでかいと、感想をたくさん書かなきゃいけない気がして億劫だ

etc, etc.

その点、最も感想を貰う事に向いたプラットフォームは新都社に代表されるウェブ漫画だと思う。 ウェブ漫画の特徴は、

  • 縦長のインタフェースで漫画が読める
  • 読み終わると末尾に感想を書けるテキストボックスがある
  • テキストボックスは一行である
  • メールアドレス記入欄も無い
  • ハンドルネームも不要
  • シャッと書いてEnterを押して終わり

等。素晴らしく「敷居が低い」と言える。 勿論、匿名故罵詈雑言が書き込まれる危険性等もあるのだが。

その他にも、感想が書きやすいプラットフォームにはニコニコ動画等が挙げられるが、 ニコニコ動画は作品数が多すぎて埋もれてしまう。 WEB漫画だと一定のクオリティーの作品を制作する敷居が上がるので、 ニコニコ動画と比べると埋もれづらいように思う。

*1:勿論作品がつまらないなら頑張って面白いと思うものを追求してくれとしか言えないが。

USB Type-Cの最も素晴らしい点はその剛性である

先日、Nexus 5Xを購入した。 巷ではNexus 5Xはバッテリーに問題があったり、 充電中にタッチパネルの反応が悪くなる等の問題点が指摘されているが、 私の環境では概ね良好である。

閑話休題

Nexus 5Xはデータ通信用のポートがUSB Type-Cポートになっているが、 このケーブルと端子が素晴らしい。

USB Type-Cの主なメリットは、

  • 端子に表裏が無い
  • ホスト・デバイスの端子の区別が無い
  • etc etc...

等のメリットが挙げられるが、 私的にはそれ以上に 端子の抗折力の向上 をメリットに挙げたい。

従来、同様の用途に用いられてきたUSB Micro-B端子は、 オス側の端子に垂直方向の負荷をかけると、 簡単に曲がってしまい、ツメの部分がうまく引っ掛からなくなり、 充電や接続に支障が出るようになってしまう。

また、これらは特定の会社が製造するケーブルに限った話では無く、 各社のケーブル共通の問題であるため、 規格上の問題であると言ってよいだろう。

一方で、USB Type-Cの場合はこの点が大幅に改善されている。 抜き差しの回数の強度だけでなく、 端子そのものの抗折力が向上している点は高く評価出来るだろう。

高階関数内で、引数の引数の型を取得する

いなむ先生とバンビちゃん先生にご助力頂いてやっと書けたコードです。多謝!

2016.01.03 追記 VC++では関数ポインタの型推論について、Variadic Templateのマッチングの優先順位がおかしいため正常に動作しません。

まず以下のコードについて考える。

template< typename Arg, typename Func >
void f( const Func& func )
{
    Arg arg;
    func( arg );
}

arg が未初期化になる場合についてはとりあえず考えない。 上記のコードでは、 Arg の型を明示的に指定しなくてはならない。 なんとかして、 Func から Arg の型を導出したい。

そこで、 std::function を引数とする関数に書き換える。

#include <functional>

template< typename R, typename A >
void f( const std::function< R(A) >& )
{
    A arg;
}

int main()
{
    std::function< void(int) > a;
    f( a );
}

上記のコードは通る。確かに通る。 が、lambdaや、ユーザ定義の関数オブジェクトを投げると、 std::function の型を明示的にしてやらないと推論に失敗して死ぬ。 型の指定を省略したいのに、これでは本末転倒である。

struct s
{
    void operator()( int ){}
};

int main()
{
    auto l = [](int){};
    auto o = s{};
    f< void, int >( l ); // ok
    f< void, int >( o ); // ok
    f( l ); // ng
    f( o ); // ng
}

しかも、 std::function は小さくないオーバーヘッドが生じる。 C++であるからには、可能な限りゼロオーバーヘッドの原則を貫きたい。


まず、関数ポインタについて。 引数と戻り値の型を取得する。

#include <functional>
#include <type_traits>
#include <iostream>
#include <boost/type_index.hpp>

namespace impl
{
    template< typename R, typename A > auto result_type_helper(   R( * )( A ) ) -> R;
    template< typename R, typename A > auto argument_type_helper( R( * )( A ) ) -> A;
}

template< typename F > using result_type   = decltype( impl::result_type_helper(   std::declval< F >() ) );
template< typename F > using argument_type = decltype( impl::argument_type_helper( std::declval< F >() ) );

void f( int ){}

int main()
{
    using namespace std;
    using boost::typeindex::type_id_with_cvr;
    using ft = decltype( f );
    cout << type_id_with_cvr< result_type<   ft > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< ft > >().pretty_name() << endl;
}

同様に関数オブジェクト。

...

namespace impl
{
    template< typename R, typename F, typename A > auto result_type_helper(   R( F::* )( A ) ) -> R;
    template< typename R, typename F, typename A > auto argument_type_helper( R( F::* )( A ) ) -> A;
    template< typename R, typename F, typename A > auto result_type_helper(   R( F::* )( A )const ) -> R;
    template< typename R, typename F, typename A > auto argument_type_helper( R( F::* )( A )const ) -> A;
}

template< typename F > using result_type   = decltype( impl::result_type_helper(   &F::operator() ) );
template< typename F > using argument_type = decltype( impl::argument_type_helper( &F::operator() ) );

struct s
{
    void operator()(char){}
};

int main()
{
    using namespace std;
    using boost::typeindex::type_id_with_cvr;
    auto l = [](double){};
    using lt = decltype( l );
    cout << type_id_with_cvr< result_type<   lt > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< lt > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   s  > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< s  > >().pretty_name() << endl;
}

メンバ関数ポインタを扱うため、人によっては見慣れない演算子 ::* が使われている。

ただし、関数オブジェクト内で関数がオーバーロードされている場合はうまく動かない。 そもそも、オーバーロードされている場合は明示的に指定しないと型が取得出来ないのは当然なので、 以下のように明示的に取得して使用する。

...

namespace impl
{
    template< typename R, typename F, typename A > auto result_type_helper(   R( F::* )( A ) ) -> R;
    template< typename R, typename F, typename A > auto argument_type_helper( R( F::* )( A ) ) -> A;
}

template< typename F > using result_type   = decltype( impl::result_type_helper(   std::declval< F >() ) );
template< typename F > using argument_type = decltype( impl::argument_type_helper( std::declval< F >() ) );

struct s
{
    void operator()(char){}
    void operator()(float){}
};

int main()
{
    using namespace std;
    using boost::typeindex::type_id_with_cvr;
    void( s::* c )( char  ) = &s::operator();
    void( s::* f )( float ) = &s::operator();
    using ct = decltype( c );
    using ft = decltype( f );
    cout << type_id_with_cvr< result_type<   ct > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< ct > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   ft > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< ft > >().pretty_name() << endl;
}

最後に、これらについて type_traits を用いて分岐を行う。 さらに、引数の型が void の場合と、2つ以上の引数に対応する。

#include <functional>
#include <type_traits>
#include <iostream>
#include <tuple>
#include <boost/type_index.hpp>

namespace impl
{
    template< typename P > struct function_pointer
    {
        template< typename R                               > static auto argument( R( * )( void       ) ) -> void;
        template< typename R                               > static auto result(   R( * )( void       ) ) -> void;
        template< typename R, typename A                   > static auto argument( R( * )( A          ) ) -> A;
        template< typename R, typename A                   > static auto result(   R( * )( A          ) ) -> R;
        template< typename R, typename A, typename... Args > static auto argument( R( * )( A, Args... ) ) -> std::tuple< A, Args... >;
        template< typename R, typename A, typename... Args > static auto result(   R( * )( A, Args... ) ) -> R;
        using argument_type = decltype( argument( std::declval< P >() ) );
        using result_type   = decltype( result(   std::declval< P >() ) );
    };
    
    template< typename P > struct member_function_pointer
    {
        template< typename R, typename C                               > static auto argument( R( C::* )( void       )      ) -> void;
        template< typename R, typename C                               > static auto result(   R( C::* )( void       )      ) -> R;
        template< typename R, typename C                               > static auto argument( R( C::* )( void       )const ) -> void;
        template< typename R, typename C                               > static auto result(   R( C::* )( void       )const ) -> R;
        template< typename R, typename C, typename A                   > static auto argument( R( C::* )( A          )      ) -> A;
        template< typename R, typename C, typename A                   > static auto result(   R( C::* )( A          )      ) -> R;
        template< typename R, typename C, typename A                   > static auto argument( R( C::* )( A          )const ) -> A;
        template< typename R, typename C, typename A                   > static auto result(   R( C::* )( A          )const ) -> R;
        template< typename R, typename C, typename A, typename... Args > static auto argument( R( C::* )( A, Args... )      ) -> std::tuple< A, Args... >;
        template< typename R, typename C, typename A, typename... Args > static auto result(   R( C::* )( A, Args... )      ) -> R;
        template< typename R, typename C, typename A, typename... Args > static auto argument( R( C::* )( A, Args... )const ) -> std::tuple< A, Args... >;
        template< typename R, typename C, typename A, typename... Args > static auto result(   R( C::* )( A, Args... )const ) -> R;
        using argument_type = decltype( argument( std::declval< P >() ) );
        using result_type   = decltype( result(   std::declval< P >() ) );
    };
    
    template< typename C > struct function_object
    {
        using argument_type = typename member_function_pointer< decltype( &C::operator() ) >::argument_type;
        using result_type   = typename member_function_pointer< decltype( &C::operator() ) >::result_type;
    };
    
    template< typename F > struct function_traits
    : public std::conditional_t
    <
        std::is_member_function_pointer< F >::value,
        member_function_pointer< F >,
        std::conditional_t
        <
            std::is_pointer< F >::value,
            function_pointer< F >,
            function_object< F >
        >
    >
    {};
}

template< typename F > using argument_type = typename impl::function_traits< std::decay_t< F > >::argument_type;
template< typename F > using result_type   = typename impl::function_traits< std::decay_t< F > >::result_type;

struct s
{
    void operator()(int){}
};

struct o
{
    void operator()(char){}
    void operator()(void){}
};

void f( float, double ){}

int main()
{
    using namespace std;
    using boost::typeindex::type_id_with_cvr;
    
    auto l = [](long){};
    void( o::* c )( char ) = &o::operator();
    void( o::* v )( void ) = &o::operator();
    
    using ft = decltype( f );
    using lt = decltype( l );
    using ct = decltype( c );
    using vt = decltype( v );
    
    cout << type_id_with_cvr< result_type<   s  > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< s  > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   lt > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< lt > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   ct > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< ct > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   vt > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< vt > >().pretty_name() << endl;
    cout << type_id_with_cvr< result_type<   ft > >().pretty_name() << endl;
    cout << type_id_with_cvr< argument_type< ft > >().pretty_name() << endl;
}

上記の関数は、引数が2つ以上の場合は std::tuple を戻す。 gcc 5.2.0 では完動するが、clang 3.8.0では std::tuple の型名を取得すると何故か以下の実行時エラーで例外が送出される。

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::runtime_error> >'
  what():  Type name demangling failed

Anker PowerLine USB-C & USB 3.0 Cableは規格に準拠していて安全らしい

Googleのエンジニアである、Benson Leung氏が、Amazonで片っ端からUSB-Cケーブルをレビューし、規格違反のケーブルを弾劾しているのは周知の事実である。

今回は、Google Nexus 5Xを購入したので、定番であるAnkerのケーブルを買う事にした。 Ankerのケーブルで、2015年12月12日現在、Benson氏の直接のレビューがあるのは以下の1製品のみ。

Anker's USB-C to USB 3.0 Cable uses a spec compliant 56kΩ identification resistor, great 3rd party SuperSpeed cable

だそうです。おすすめ。

std::as_const と sprout::as_cont の違い

std::as_constsprout::as_const は、何れも引数を const 修飾された型に変換する関数テンプレートである。尚、 std::as_constC++17にむけて提案されている関数であるため、実装されている環境は少ない。

Constant View: A proposal for a std::as_const helper function template

sprout::as_const はSprout全体において運用される、処理をコンパイル時に行うための記述が含まれるが、それについては本記事の主旨ではないため触れない*1

std::as_constsprout::as_const の最も顕著な違いは、 sprout::as_const は実引数が rvalue の時、戻り値の型が rvalue reference になるのに対し、 std::as_const は実引数が rvalue であっても戻り値の型は lvalue reference となる。

rvalue 対応版

#include <type_traits>
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr; 

template< typename Arg >
std::conditional_t
<
    std::is_lvalue_reference< Arg >::value,
    std::add_lvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > >,
    std::add_rvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > >
>
inline as_const( Arg&& arg )noexcept
{
    return std::forward< Arg >( arg );
}

template< typename Arg > void f( Arg&& arg )
{
    cout << "arg is " << type_id_with_cvr< decltype( arg ) >() << endl;
}

int main()
{
    int a;
    f( a );
    f( move( a ) );
    f( as_const( a ) );
    f( as_const( move( a ) ) );
}

出力

Start
arg is int&
arg is int&&
arg is int const&
arg is int const&&
0
Finish

当然ながら、 std::forwardstd::move を適切に使用する必要がある。

lvalue

...
template< typename Arg >
std::add_lvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > >
inline as_const( Arg&& arg )noexcept
{
    return std::forward< Arg >( arg );
}
...

出力

Start
arg is int&
arg is int&&
arg is int const&
arg is int const&
0
Finish

何れも簡易的な実装であり、提案された文書とは実装が異なる。

初期の提案では右辺値を渡された場合の変数の寿命延長について考慮し、 右辺値を受け取った場合はコピー演算を行うバージョンについても記述されていたが、 最新の文書では無くなっているため問題ないと判断されたのであろう。

右辺値をどう扱うかの違いによって生じる影響については機会があればまた論じたい。

私はSproutの右辺値も扱えるバージョンが好きだな。

*1:というか私が理解してない

cv修飾された仮引数はUniversal Referenceになれない

以下のコードは合法である。

#include <type_traits>
#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr; 

template< typename Arg > void f( Arg&& arg )
{
    cout << "`Arg`   is `" << type_id_with_cvr< Arg >() << "`" << endl;
    cout << "`Arg&&` is `" << type_id_with_cvr< decltype( arg ) >() << "`" << endl;
}

int main()
{
    int a;
    f( a );
    cout << "================" << endl;
    f( move( a ) );
}

出力

Start
`Arg`   is `int&`
`Arg&&` is `int&`
================
`Arg`   is `int`
`Arg&&` is `int&&`
0
Finish

一方で、関数 f の引数にcv修飾を行うとコンパイルエラーとなる。

...
template< typename Arg > void f( Arg const&& arg )
...

出力

Start
prog.cc:18:5: error: no matching function for call to 'f'
    f( a );
    ^
prog.cc:9:31: note: candidate function [with Arg = int] not viable: no known conversion from 'int' to 'const int &&' for 1st argument
template< typename Arg > void f( Arg const&& arg )
                              ^
1 error generated.
1
Finish

型推論と実際の型

仮引数の宣言 実引数 Arg の型 arg の型
Arg&& arg a int& int&
Arg&& arg std::move( a ) int int&&
Arg const&& arg a int int const&&
Arg const&& arg std::move( a ) int int const&&

表を見れば明白な様に、Reference Collapsingは直接的原因ではない。 コンパイルエラーの原因は、仮引数の型が int const&& の関数に対し、渡された実引数が lvalue であるためであり、 更にその原因は、テンプレート仮引数の型推論において、仮引数の宣言が Arg const&& arg の時、 Arg の型が int に推論されているためである。

直感的には、 Argint& に推論され、 arg に対して lvalue が束縛されて欲しい所だが、 表を見れば、そもそも仮引数の宣言が Arg&& arg かつ実引数が a の時、 仮引数の宣言が参照であるにも関わらず、 Arg の型が int& に推論されている状況が特殊である事が分かる。

ここで規格先生にご登場願う。 以下は項 14.8.2.1 より引用。

3
If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cvunqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. [ Example:
template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
               // would bind an rvalue reference to an lvalue
— end example ]

以下翻訳

3
仮引数がcv修飾された型の場合、最初の仮引数の型へのcv修飾子は型推論の段階で無視される。もし仮引数が参照型の場合、仮引数における参照部分は型推論に使われる。もし仮引数がcv修飾されていない rvalue reference であり、実引数が lvalue の場合、型推論には型「実引数への lvalue reference 」が実引数の代わりに使われる。

つまり、cv修飾されてない Arg&& arg が特別扱いされるのであり、仮引数がcv修飾されている場合、通常の型推論の規則、即ち 「関数テンプレートのテンプレート型引数の推論において、推論される型引数は参照を除去した型になる」という規則が適用される。