altebute.hatenablog.com

犬も歩けば規格にあたる

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

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

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修飾されている場合、通常の型推論の規則、即ち 「関数テンプレートのテンプレート型引数の推論において、推論される型引数は参照を除去した型になる」という規則が適用される。

参照型に対するcv修飾子はill-formedまたは無視される

const int&const int への参照であるため、well-formedであるが、 int& constint&に対するcv修飾であるためill-formedである。
ただし、cv修飾される型名が typedef-name または decltype-specifier の場合、cv修飾子は無視される。
また、同時に reference collapsing が起きる場合も、cv修飾子は同様に無視される。

このため、明示的にcv修飾を行った場合も、予想しない型になる場合がある。

#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()
{
    cout << "================================" << endl;
    cout << "`Arg`         is `" << type_id_with_cvr< Arg         >() << "`." << endl;
    cout << "`Arg&`        is `" << type_id_with_cvr< Arg&        >() << "`." << endl;
    cout << "`Arg&&`       is `" << type_id_with_cvr< Arg&&       >() << "`." << endl;
    cout << "--------------------------------" << endl;
    cout << "`Arg const`   is `" << type_id_with_cvr< Arg const   >() << "`." << endl;
    cout << "`Arg const&`  is `" << type_id_with_cvr< Arg const&  >() << "`." << endl;
    cout << "`Arg const&&` is `" << type_id_with_cvr< Arg const&& >() << "`." << endl;
}

int main()
{
    f< int         >();
    f< int&        >();
    f< int&&       >();
    f< const int   >();
    f< const int&  >();
    f< const int&& >();
    cout << "================================" << endl;
    cout << "voltile work same as const." << endl;
}

出力

> Start
================================
`Arg`         is `int`.
`Arg&`        is `int&`.
`Arg&&`       is `int&&`.
--------------------------------
`Arg const`   is `int const`.
`Arg const&`  is `int const&`.
`Arg const&&` is `int const&&`.
================================
`Arg`         is `int&`.
`Arg&`        is `int&`.
`Arg&&`       is `int&`.
--------------------------------
`Arg const`   is `int&`.
`Arg const&`  is `int&`.
`Arg const&&` is `int&`.
================================
`Arg`         is `int&&`.
`Arg&`        is `int&`.
`Arg&&`       is `int&&`.
--------------------------------
`Arg const`   is `int&&`.
`Arg const&`  is `int&`.
`Arg const&&` is `int&&`.
================================
`Arg`         is `int const`.
`Arg&`        is `int const&`.
`Arg&&`       is `int const&&`.
--------------------------------
`Arg const`   is `int const`.
`Arg const&`  is `int const&`.
`Arg const&&` is `int const&&`.
================================
`Arg`         is `int const&`.
`Arg&`        is `int const&`.
`Arg&&`       is `int const&`.
--------------------------------
`Arg const`   is `int const&`.
`Arg const&`  is `int const&`.
`Arg const&&` is `int const&`.
================================
`Arg`         is `int const&&`.
`Arg&`        is `int const&`.
`Arg&&`       is `int const&&`.
--------------------------------
`Arg const`   is `int const&&`.
`Arg const&`  is `int const&`.
`Arg const&&` is `int const&&`.
================================
voltile work same as const.
0
Finish

以下は N3797 項 8.3.2 より引用。

1
In a declaration T D where D has either of the forms & attribute-specifier-seqoptD1 && attribute-specifier-seqoptD1 and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T,” then the type of the identifier of D is “derived-declarator-type-list reference to T.” The optional attribute-specifier-seq appertains to the reference type. Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name (7.1.3, 14.1) or decltype-specificer (7.1.6.2), in which case the cv-qualifiers are ignored. [ Example: typedef int& A; const A aref = 3; // ill-formed; lvalue reference to non-const initialized with rvalue The type of aref is “lvalue reference to int”, not “lvalue reference to const int”. ? end example ] [ Note: A reference can be thought of as a name of an object. ? end note ] A declarator that specifies the type “reference to cv void” is ill-formed.

6
If a typedef-name (7.1.3, 14.1) or a decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR.

Android 6.0 Marshmallowでアスキーアートを表示する

ブートループになっても知りません。 分からない所は調べてください。

大まかな順序

  1. Androidの開発者向け環境を整え、パスを通す
  2. adb reboot bootloader
  3. twrpを送信しブート( fastboot boot )
  4. twrp上で/systemをマウント
  5. adb pull /system/etc/fonts.xml
  6. fonts.xmlを書き換え
  7. adb push fonts.xml /system/etc
  8. adb push textar.ttf /system/fonts
  9. 書き換えたファイルの属性が644であることを確認
  10. adb reboot

書き換え内容

    ...
    <family name="sans-serif">
        <font weight="100" style="normal">Roboto-Thin.ttf</font>
        ...
        <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
    </family>

    <family name="MS PGothic">
        <font weight="400" style="normal">textar-min.ttf</font>
    </family>

    <alias name="MS Pゴシック" to="MS PGothic" />

    <!-- Note that aliases must come after the fonts they reference. -->
    ...

注意

  • ...は単なる中略
  • 文字コードUTF-8
  • タブではなく半角スペース4つ
  • 改行はLF

adb pushの際に、systemパーティションの容量不足で失敗する場合がある。

ワカラナイ シラナイ コムギコカナニカダ

マルチディスプレイ時に稀に一部のディスプレイが表示されない場合の対処法

普段デュアルディスプレイ環境を使用しているのだが、稀にサブディスプレイが表示されない場合がある。

傾向を察するに、アナログディスプレイとの接続時に、ディスプレイとグラフィックスカードの相性が悪いと、接続を検知出来ないのかもしれない。

多くの場合、再起動したり、スリープから復帰すると戻るのだが、うまくいかないケースもあり面倒だ。

以下の方法で簡単かつ素早く解決出来るケースが多い。

  1. デバイス マネージャーを起動する
  2. ディスプレイ アダプターのツリーを展開
  3. 使用しているデバイスのプロパティを開く
  4. ドライバータブを開く
  5. 無効(D)ボタンを押す
  6. 再度有効にする