GNU screen をマウスでスクロールさせる方法

~/.screenrc に以下の一行を加えるだけ

termcapinfo xterm* ti@:te@


これでマウスのスクロール(マウスホイールや,macトラックパッドのmulti-touchのドラッグ)で GNU screen をスクロールできます

デフォルト設定のGNU screenだと,スクロールは "C-a" "C-[" でコピーモードに遷移して,それから"C-b"とか"C-f"とか"Command-↑"とかで,結構ストレスが溜まります.mac book とかノートPCで作業するときは上記設定でトラックパッドでスクロールした方が便利です

SFINAEでオーバーロードされた関数を検出する方法

C++ のSFINAE (Substitution Failure Is Not An Error)を使って,クラス T に foo(int a, int b)があるか否かを調べる方法です.

簡単な例(class Tに オーバーロードされた関数がない場合)

まずはシンプルに,クラス T に対してT::foo が存在するかどうか調べるコードを用意します.

SFINAEの実装は,c++11で導入された type_traits の std::false_type と std::true_type を使うと簡単に実装できます.

(めんどくさいのでクラスは classじゃなくてstruct で定義しています)

#include <iostream>
#include <type_traits>

template <typename... Ts> using void_t = void;  // std::void_t が使えない処理系もあるので自前で void_t を定義

// デフォルトのテンプレートクラス
// その他のテンプレートが展開できなかった場合は,このデフォルトのテンプレートが採用される
// 
// このテンプレートクラスは std::false_type を継承しているので,::value は false となる.
template <typename T, typename = void>
struct has_foo : std::false_type {};

// T::foo が存在する場合は,こちらのテンプレートが展開される
//
// このテンプレートクラスは std::true_type を継承しているので, ::valueはtrueとなる
template <typename T>
struct has_foo<T, void_t<decltype(&T::foo)>> : std::true_type {};


int main() {
    struct A {
	void foo();
    };

    struct B {
	void another();
    };

    std::cout << std::boolalpha;
    
    std::cout << has_foo<A>::value << std::endl;  
    // true が出力される.つまり struct Aにはfooがある
    
    std::cout << has_foo<B>::value << std::endl; 
    // false に出力される.つまり struct  Bにはfooが無い

    return 0; 
}

オーバーロードされた関数がある場合の挙動 (+ SFINAEの仕組み)

上記のコードは,オーバロードされた関数がなければ期待通りに動作します.

一方で overload された関数がある場合, has_foo<T> は T::foo を検出できなくなります.例えば

    struct C {
        void foo();
        void foo(int x);
    };
    std::cout << has_foo<C>::value << std::endl;  

は true(つまり C::foo がある) ではなくて,false (つまりC::foo がない)を出力します

理由は has_foo の decltype(&T::foo) で T::foo のポインタが解決できないためです.
例えば g++ で

    struct C {
        void foo();
        void foo(int x);
    };
     decltype(C::foo);

というコードをコンパイルすると以下のエラーが出ます

error: 'decltype' cannot resolve address of overloaded function
         decltype(C::Foo);

つまりT::foo にはオーバーロードされたものが二つあるので,コンパイラはどちらのアドレスを選べば良いか判断できずコンパイル・エラーを出します.一方でSFINAEを使うとこのエラーを回避するために,デフォルトの has_foo<T> が選ばれてコンパイルが進むということになります.

オーバーロードされた関数がある場合のSFINAE

少々面倒なコードが必要になります

#include <type_traits>

// TestType<A,int> で A::foo(int) のdecltypeを返す
template<typename T, typename... Ts>
using TestType = auto(Ts...) -> decltype(T::foo(std::declval<Ts>()...));


//  T::foo()を検出するテンプレート
template<typename T, typename = void>
struct has_foo0 : std::false_type { };
template<typename T>
struct has_foo0<T, std::void_t<TestType<T>> > : std::true_type { };

//  T::foo(int)を検出するテンプレート
template<typename T, typename = void>
struct has_foo1 : std::false_type { };
template<typename T>
struct has_foo1<T, std::void_t< std::is_same<TestType<T,int>,void(int)>> > : std::true_type { };

TestType<T, ...Ts> というテンプレートクラスを新たに用意します.これは例えば TestType<A,int > とすると A::foo(int)のdecltypeを返します.これを使って has_foo0<T> と has_foo1<T>を定義しています

テストしてみます

#include <iostream>

//
// 上記のテンプレートの定義(省略)
//

struct A{
    static void foo();
    static void foo(int x);
};
struct B{
    static void foo();
};
struct C{
};

void f();

int main()
{
    static_assert(      has_foo0<A>::value );
    static_assert(      has_foo0<B>::value );
    static_assert(not has_foo0<C>::value );   // struct C に C::foo() は無い
    static_assert(not has_foo0<int>::value ); // intにも int::foo()は無い

    static_assert(      has_foo1<A>::value );   // foo(int) は struct Aのみにある
    static_assert(not has_foo1<B>::value );
    static_assert(not has_foo1<C>::value );
    static_assert(not has_foo1<int>::value );
    return 0;
}

正しく T::foo() や T::foo(int)が検出できるようになりました!

補足:コンパイラのバージョンについて

今回のコードは,とりあえず C++17 を使って書きました.
type_traits のstd::true_type やら variadic templates を使っているのでコンパイルには C++11以降が必須だと思います.

debian の nvidia-graphics-drivers パッケージを自分で編集&更新する方法

debiannvidia-graphics-drivers が古いまま(470.57.02 )でなかなか更新されないので,自力で 470.63.01 に更新しました.

470.57.02 はLinux-5.14をサポートしていませんが(モジュールがビルドできない), 470.63.01 ならLinux-5.14もサポートしています.

更新の手順(その1:ソースのダウンロードと展開)

まずapt-getでパッケージのソースを入手します

$ apt-get source nvidia-graphics-drivers

2021年9月16日の時点で nvidia-graphics-drivers のバージョンは 470.57.02 でした.

ソースのディレクトリに移動します

$ cd nvidia-graphics-drivers-470.63.01

uscanを使ってnvidiaが公開している最新版ドライバのバージョンを確認します

$ uscan --report

最新版は 470.63.01 でした

nvidiaのサイトから470.63.01のソースをダウンロードします

$ ./debian/rules get-orig-source

これで
nvidia-graphics-drivers_470.63.01.orig-amd64.tar.gz
nvidia-graphics-drivers_470.63.01.orig-arm64.tar.gz
が生成されます

生成されたファイルから *.run をとりだして,nvidia-graphics-drivers-470.63.01直下にあるamd64ディレクトリと arm64ディレクトリに runファイルをコピーします

このように配置されればOKです

$ ls  a*64/
amd64/NVIDIA-Linux-x86-470.63.01.run
arm64/NVIDIA-Linux-aarch64-470.63.01.run

更新の手順(その2:changelogの修正)

debian/changelogを修正します

$ debchange --distribution local --newversion 470.63.01-1
  • "distribution" は利用している環境に合わせます.
  • "newversion"は "ドライバのバージョン番号"-"リリース番号"の形式になります

更新の手順(その3: ビルド)

$ dpkg-buildpackage -us -uc -rfakeroot

更新の手順(その4:パッケージの修正)

ビルドするとエラーが出ます.理由は, 470.63.01 で新たに導入されたライブラリlibnvidia-vulkan-producer.so.470.63.01 に対する設定が debian/* 以下に存在しないためです.

とりあえず libnvidia-vulkan-producer.so.470.63.01 は無視することにして,debian/not-installed.in に libnvidia-vulkan-producer.so.470.63.01を追記します.

これで libnvidia-vulkan-producer.so.470.63.01はインストールされなくなります*1

更新の手順(その5: 再ビルド)

$ dpkg-buildpackage -us -uc -rfakeroot

今度はビルドが通りました

動作確認

生成された *.deb を通常通りにインストールするだけです

注意事項

上記の手順で問題なくXorgは起動し、opengl のプログラムも正常に動作しています.

ただし libnvidia-vulkan-producer.so.470.63.01 をインストールしていないので,何かのきっかけでエラーが出る可能性があります.不具合がある場合はdebian/not-installed.in を使わずに適切に libnvidia-vulkan-producer.so.470.63.01 をインストールするか,正式なパッケージが公開されるまで待ちましょう.

*1:豪快な方法ですがこれでも動作するので今回はこれで良しとしています

ムービーファイルから特定のチャプターだけを抜き出す方法

mp4などの動画ファイルから,特定の章(チャプター)を切り出す方法です.

切り出しはffmpegを使うと簡単かつ高速です.音声・画質の劣化もありません.

手順1) チャプター情報の確認

ffmpeg付属のffprobeコマンドを使いチャプターの一覧を確認します

$ ffprobe -i 動画ファイル名 

チャプターの一覧は次のような書式になります

  Chapters:
    Chapter #0:0: start 開始時刻, end 終了時刻
      Metadata:
        title           : タイトル1
    Chapter #0:0: start 開始時刻, end 終了時刻
      Metadata:
        title           : タイトル2

これでチャプターを抽出したいチャプターの開始時刻,終了時刻を確認します

手順2) 切り出し

開始時刻,終了時刻を指定して,ffmpegコマンドを使います

$ ffmpeg  -ss 開始時刻  -to 終了時刻  -i 元動画のファイル名 -c copy -map 0 -map_chapters -1  新しい動画のファイル名

"-c copy"で,動画の再圧縮なしで新しい動画を作成します.これで画像・音声の劣化は回避できます

補足

シェルスクリプトで上記処理を自動化する際は"-show_chapters"や " -print_format csv " オプションを使うと便利です

$ ffprobe -i 元動画のファイル名 -show_chapters

"-show_chapters"を付与すると,以下の出力が得られます

[CHAPTER]
id=0
time_base=1/10000000
start=開始時刻(単位 time_base)
start_time=開始時刻(単位 秒)
end=終了フレーム番号(単位 time_base)
end_time=終了時刻(単位 秒)
TAG:title=タイトル
[/CHAPTER]

ffprobeは詳細情報を大量に /dev/stderr に出力します."2>/dev/null"をつけます

$ ffprobe -i 元動画のファイル名 -show_chapters 2>/dev/null


さらに"-print_format csv"をつけると,カンマ(,)区切りのテキスト形式に書式が変わります

$ ffprobe -i 元動画のファイル名 -show_chapters -print_format csv 2>/dev/null

シェルスクリプトの場合はcutコマンドでパースすると良いでしょう

$ ffprobe -I 元動画のファイル名 -show_chapters -print_format csv  2>/dev/null  | cut -d ',' -f "5,7,8"
$ ffprobe -I 元動画のファイル名 -show_chapters -print_format csv  2>/dev/null  \
    | cut -d ',' -f "5,7,8" \
    | sed -e 's|,| |g' \
    | while read start end title; do 
   echo $start  $end $title
done

python / numpy で特異値分解(SVD)を使って一般化逆行列を計算する

一般化逆行列(擬似逆行列,最小二乗法に対応するやつ)を python で計算するサンプル

特異値分解(SVD)が必要なので,実装は numpy を使います

c++/eigen で実装したコード https://pyopyopyo.hatenablog.com/entry/2021/09/07/090000python版になります

import numpy as np

A = np.random.random((3,3))

# SVDを使って,A を U @ S @ Vh の形に分解する
U,S,Vh = np.linalg.svd(A)

# 検算. U @ np.diag(S) @ Vh は A と一致する
assert np.allclose(U@np.diag(S)@Vh, A)

# 一般化逆行列を計算する
#  A = U @ np.diag(S) @ Vh
# だから
# A+ = Vh.T @ np.diag(1./S) @ U.T
#
Ainv = Vh.T @ np.diag(1./S) @ U.T

# 検算.  A と A+ の積 m は単位行列に一致する
m = A@Ainv
assert np.allclose(m, np.identity(3))

C++/Eigen で特異値分解(SVD)を使って一般化逆行列を計算する

一般化逆行列(擬似逆行列,最小二乗法に対応するやつ)をc++で計算するサンプル

特異値分解(SVD)が必要なので,実装は Eigen を使います

python/numpy の実装も用意しました.こちらです https://pyopyopyo.hatenablog.com/entry/2021/09/14/161955

#include <Eigen/Dense>
#include <iostream>
#include <random>
#include <assert.h>

int
main()
{

    //ランダムな 3x3 行列を生成

    std::random_device rd;
    std::mt19937 gen(rd());
    std::normal_distribution<float> dis(0, 10);
    //ラムダ式で乱数を生成,生成した値を行列の各要素に格納する
    Eigen::MatrixXf A = Eigen::MatrixXf::NullaryExpr(3,3,  
                              [&](){return dis(gen);});

    // std::cout << "A: " << A << std::endl;

    Eigen::JacobiSVD<Eigen::MatrixXf> svd(A,
					  Eigen::ComputeFullU
					  | Eigen::ComputeFullV);

    //std::cout << "U: " << svd.matrixU() << std::endl;
    //std::cout << "S: " << svd.singularValues()  << std::endl;
    //std::cout << "V: " << svd.matrixV() << std::endl;


    // 一般化逆行列を計算する
    //
    //
    //  svdで  行列A を U * S * V.T と分解しているから
    //
    //  一般化逆行列 A+  は V * S.T * U.T

    Eigen::VectorXf s = svd.singularValues();
    s = s.array().inverse();

    // 一般化逆行列の変数名は Ainv 
    Eigen::MatrixXf Ainv =
                svd.matrixV()
             * s.asDiagonal()  
             * svd.matrixU().transpose();

    // A と A+ をかけると単位行列になる
    Eigen::MatrixXf m= A * Ainv;
    std::cout << m << std::endl;

    // 実際は,計算誤差があるので「ほぼ」単位行列になる.
    // そこで mとIdentity(3,3)が「ほぼ」等しいかEigenの isApprox() で確認
    // 結果は assert()でチェック

    assert( Eigen::MatrixXf::Identity(3,3).isApprox(m) );

    return 0;

/dev/null みたいな std::ostream

/dev/null みたいな std::ostream を作る方法.

方法1 : std::ostreamを継承して自前の null stream を作成する

以下のURLで紹介されている方法
stackoverflow.com

#include <iostream>

class NulStreambuf : public std::streambuf
{
    char dummyBuffer[ 64 ];
protected:
    virtual int overflow( int c )
    {
        setp( dummyBuffer, dummyBuffer + sizeof( dummyBuffer ) );
        return c == traits_type::eof()?'\0':c;
    }
};
class NulOStream : private NulStreambuf, public std::ostream
{
public:
    NulOStream() : std::ostream( this ) {}
    NulStreambuf* rdbuf() const { return (NulStreambuf*)this; }
};

int main()
{
    bool  output_enabled=false;

    NulOStream nos;
    std::ostream &os = (output_enabled)?std::cout : nos;

    os << "Hello" << std::endl;
    os << "c++" << std::endl;

    return 0;
}

方法2:boostの boost::iostreams::null_sink を使う

boostに実装がある.詳細は boost/iostreams/device/null.hpp あたりを読めばわかるので,割愛

方法3: マクロで回避

実際のコードは,多くの場合以下のような形になる

bool output_enabled = false;

if (output_enabled)
    std::cout << "Hello" << std::end;

if (output_enabled)
    std::cout << "c++" << std::end;

と言うわけで

#define LOG if (output_enabled) std::cout 

bool output_enabled = false;

LOG << "Hello" << std::end;
LOG << "c++" << std::end;

実はこれだけで解決する


こう言う形でもいいかも

#define LOG if (x) std::cout 

bool output_enabled = false;

LOG(output_enabled) << "Hello" << std::end;
LOG(output_enabled) << "c++" << std::end;