() 括弧、丸括弧
Parentheses、parenthesisの複数形
{} 波括弧、中括弧
Curly brackets、日本語では中括弧ではなく波括弧というのが正確らしい。
[] 角括弧
Square brackets
<> 山括弧
Angle bracketまたはChevron
Parentheses、parenthesisの複数形
Curly brackets、日本語では中括弧ではなく波括弧というのが正確らしい。
Square brackets
Angle bracketまたはChevron
忘備録。Visual Studio CodeはWindows側で実行している。
ここではubuntuを使っている。
デフォルトではソースパッケージが無効になっているので、/etc/apt/sources.list
を開いて、deb-srcで始まる行のコメントを削除する。
fortuneというコマンドラインアプリがあるので、これのソースコードと取得し、wsl内でビルドしておく。ディレクトリは/mnt/内(Windowsとwslで両方から操作できる)で行うこと。
1 2 3 4 5 6 7 8 9 10 |
$ mkdir /mnt/y/work/fortune $ cd /mnt/y/work/fortune/ $ sudo apt-get update $ which fortune /usr/games/fortune $ dpkg-query --search /usr/games/fortune fortune-mod: /usr/games/fortune $ sudo apt-get build-dep fortune-mod $ apt-get source fortune-mod $ cd fortune-mod-1.99.1 |
これでソースが取得できたので、これをデバッグビルドする。このアプリの場合はMakefileがついていて’make debug’ができるようなので実行する。
が、これがエラーになるのでCFLAGSを指定してビルドした。
1 2 3 4 5 |
$ make clean $ CFLAGS="-g -DDEBUG" LDFLAGS="" make $ fortune/fortune Don't look now, but the man in the moon is laughing at you. $ |
これでwsl側の準備ができた。
VSCodeでフォルダY:\work\fortune\fortune-mod-1.99.1を開き、デバッグ環境C++(GDB/LLDB)でlaunch.jsonを開き以下を入力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "C++ Launch", "type": "cppdbg", "request": "launch", "program": "/mnt/y/work/fortune/fortune-mod-1.99.1/fortune/fortune", "args": [""], "stopAtEntry": true, "cwd": "/mnt/y/work/fortune/fortune-mod-1.99.1", "environment": [], "externalConsole": true, "windows": { "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] }, "pipeTransport": { "pipeCwd": "", "pipeProgram": "c:\\windows\\sysnative\\bash.exe", "pipeArgs": ["-c"], "debuggerPath": "/usr/bin/gdb" }, "sourceFileMap": { "/mnt/y": "y:\\" } }, ] } |
HEVC(High Efficiency Video Coding)はMPEGによって開発された。
最大効率でAVC(H264)の2倍。動画のサイズが半分になる。
fourccはhvc1またはhev1
1 |
$ ffmpeg -i orig.mkv -c:v libx265 -c:a copy h265-normal.mkv |
サイズを犠牲にして品率を高める割合。低いほうが品質が高い。デフォルトは28、0から51まで指定可能
0だとロスレスだけどファイルサイズが大きくなる、51が最悪だけどファイルサイズが小さい。
圧縮効率を指定してエンコード時間を調節する機能。以下のうちの一つを指定。
superfast, veryfast, faster, fast, medium, slow, slower, veryslow
ex)
1 |
$ ffmpeg -i orig.mkv -c:v libx265 -preset veryslow -c:a copy h265-veryslow.mkv |
実際にこれをやってもサイズは小さくならなかった。なぜ?
コンパイラに分岐予測最適化を提供するためのマクロ。ifの中で使い、trueになりそうなときはQ_LIKELY、falseになりそうなときはQ_UNLIKELYをつかう。Visual Studioコンパイラだと特に何もしないが、gccだと__builtin_expectに展開される。
1 2 3 |
if (Q_UNLIKELY(!get())) { return false; } |
絶対に到達しない場所に記述する。switch文ですべてのenumをcaseに書かないと警告が出る場合にこれを置いておく。
関数の引数で使わないものを書いておくと警告が出なくなる。関数の引数の変数を消してもいい。
実行時にtrueでなければならない式を記述してデバッグ時にエラーが出ないことを確認する。
_Xの方は警告文をいろいろ書ける。
式がtrueであることをコンパイルに教えて最適化に役立てる。falseを指定すると、Q_UNREACHABLEと同じになる。
デバッグ出力する。
std::mapなどで自作のクラスをキーにする場合、そのクラスはoperator<を定義していなければならない。クラスにメンバーが1つしかない場合は、それを比較するだけで簡単なのだが、2つ以上あるときは少し難しくなる。例えばメンバーa,bがある場合、以下のように書けない。
1 2 3 |
bool operator<(const C& that) const { return a_ < that.a_ && b_ < that.b_; } |
以下のようなoperator<検証プログラムを書くとassertに引っかかる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <iostream> #include <thread> #include <random> #include <cassert> #include <tuple> using namespace std; class C { int a_; int b_; public: C(int a, int b) : a_(a), b_(b) { cout << "C()" << "a_=" << a_ << "," << "b_=" << b_ << endl; } C(const C& c) { cout << "C(const C&)" << endl; } C(C&& c) { cout << "C(const C&&)" << endl; } ~C() { cout << "~C()" << endl; } bool operator==(const C& that) const { return a_==that.a_ && b_==that.b_; } bool operator!=(const C& that) const { return !(*this==that); } bool operator<(const C& that) const { return a_ < that.a_ && b_ < that.b_; } }; int main() { // https://stackoverflow.com/a/7560564 std::random_device rd; // obtain a random number from hardware std::mt19937 eng(rd()); // seed the generator std::uniform_int_distribution<> distr(0, 9); // define the range for(int i=0 ; i < 100; ++i) { C c1(distr(eng),distr(eng)); C c2(distr(eng),distr(eng)); // check operator< is valid if (c1 < c2) { assert(c1 != c2); assert(!(c2 < c1)); } else if(c2 < c1) { assert(c1 != c2); assert(!(c1 < c2)); } else { assert(c1==c2); } } } |
例えば
C(1,6) < C(7,0)
は
1 < 7 && 6 < 0 で
false
逆にすると
C(7,0) < C(1,6)
7 < 1 && 0 < 6 で
両方falseになってしまう。
ちゃんと書くと以下のようになる。
1 2 3 4 5 6 7 |
if(a_ < that.a_) return true; if(a_ == that.a_) return b_ < that.b_; return false; |
この書き方だと3つになった場合分けがわからなくなるので、std::tieを使ってスマートに書ける。
1 |
return tie(a_,b_) < tie(that.a_, that.b_); |
アルゴリズムとしては==の代わりに !< を使って1つずつ比較していく。
このような比較はlexicographical_compareといって、イテレータを渡して実現することもできる。このコードでイテレータを渡すのは強引だが以下のようになる。
1 |
lexicographical_compare(&a_, (&b_)+1, &that.a_, (&that.b_)+1); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> #include <thread> using namespace std; // スレッドで実行される void threadfunc() { cout << "Hello World!" << endl; } int main() { // スレッドを作成して実行 thread th(threadfunc); // スレッド終了を待機 th.join(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> #include <thread> using namespace std; // スレッドで実行される void threadfunc(int i) { cout << i << endl; } int main() { // スレッドを作成して実行 thread th(threadfunc, 123); // スレッド終了を待機 th.join(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <iostream> #include <thread> using namespace std; class C { public: C() { cout << "C()" << endl; } C(const C& c) { cout << "C(const C&)" << endl; } C(C&& c) { cout << "C(const C&&)" << endl; } ~C() { cout << "~C()" << endl; } }; // スレッドで実行される void threadfunc(C c) { cout << __FUNCTION__ << endl; } int main() { // スレッドを作成して実行 thread th(threadfunc, C()); // スレッド終了を待機 th.join(); } |
実行結果
1 2 3 4 5 6 7 8 |
C() C(const C&&) ~C()C(const C&&) threadfunc ~C() ~C() Press <RETURN> to close this window... |
スレッドで実行しているので改行が合わない。がコンストラクタは以下のように呼ばれている。ユニバーサル参照のコンストラクタしか呼ばれていない。
std::threadは基本的な機能しか提供していないようで高度なことはできないようだ。例えばスレッドを開始しなかったり、スタック領域をしていしたり、スレッドをKILLしたり、ただnativehandleで処理系依存のスレッドハンドルが取得できるのでKILLくらいならできる。スレッド関数の戻り値には意味がない。スレッドから値を取得したい場合は引数経由で行う。
KILLしてみたコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include <iostream> #include <thread> #include <Windows.h> using namespace std; class C { public: C() { cout << "C()" << endl; } C(const C& c) { cout << "C(const C&)" << endl; } C(C&& c) { cout << "C(const C&&)" << endl; } ~C() { cout << "~C()" << endl; } }; // スレッドで実行される void threadfunc(C c) { // 無限ループ for(int i=0; ;++i) cout << __FUNCTION__ << i <<endl; } int main() { // スレッドを作成して実行 thread th(threadfunc, C()); // 2秒スリープ std::this_thread::sleep_for(2s); // スレッドを強制終了 if(TerminateThread(th.native_handle(), -1)) { // なぜかこれが動かない cout << "Thread Killed" << endl; } else { cerr << "Failed to kill thread" << endl; exit(1); } // スレッド終了を待機、本来すぐ処理が戻るはずだがなんかおかしい th.join(); } |
上のコードはうまく動かなくなる。coutがおかしくなってしまうのだと思われる。
というわけであんまり使えなそうである。スレッド関数はあくまでも計算とかを実行し、入出力に使うのは基本的に良くない。
C++のコンパイルは時間がかかる。ソースファイルでインクルードされている全てのファイルをパースしなければならないため。C++ではヘッダに実装を書く事も多くなりさらに重くなっている。
プリコンパイルヘッダとはソースコードの共通の最初の部分をあらかじめコンパイルしておきそれを共通で利用する方法。最初の部分は共通でないとならないので、その共通の部分を1つのヘッダファイルにしておき、ソースコードではそれをファイルの先頭でインクルードすることで利用する。経験上ではMicrosoftのclコンパイラだととても早くなる。
この共通の部分には普通は他のライブラリのヘッダファイル、例えばWindows.hやstringやvectorなどの標準ライブラリなど自分で編集しないファイルをおく。
Visual Studioでソリューションを作成すると自動でこのプリコンパイルをやってくれるが、QtのQt Creatorなどではやってくれないのでその方法。
以下の行を*.proファイルに追加する。
1 |
PRECOMPILED_HEADER = stable.h |
stable.hの名前は好きでいいはず。Visual StudioなどではStdAfx.hやpch.hに相当する。
stable.hを新規にプロジェクトに追加してそこに共通の#includeを書く、QtなのでQで始まるファイルが多くなると思われる。ここではソースファイル郡から検索する方法を紹介。
以下のコマンドはsrc/以下から全てのファイルを検索してインクルードを見つける。
1 |
$ grep -h '^\s*#include\s*<' src/ -R | sort | uniq |
-hはファイル名を表示しないオプション。このコマンドで表示されたものをstable.hに書く。以下は例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef STABLE_H #define STABLE_H #include <QApplication> #include <QDesktopWidget> #include <QDir> #include <QFileDialog> #include <QMainWindow> #include <QMessageBox> #include <QSessionManager> #include <QSettings> #include <QStandardPaths> #include <QTextStream> #endif // STABLE_H |
これでプリコンパイルの準備は出来た。Visual Studioとは違いソースコードでstable.hをインクルードしなくても良い。勝手にやってくれるようだ。ソースコード中の今までのインクルードは別にそのままでも良い、というかそのままにしておくべき。プリコンパイルを利用できない環境だと困るため。通常はすでにインクルードされているものは除外するように記述されているので問題ない。
ビルドをして早くなったか確認する。
このAPIを使うのが一番簡単。shlwapi.hとshlwapi.libが必要。
これが最も原始的やり方だと思う。kernel32にある。
System::IO::DriveTypeを使う。
C++/CLIの例
1 2 3 4 5 |
System::IO::DriveInfo di(path); if(di.DriveType == System::IO::DriveType::Network) { // path is on Network drive } |
関数と関数ポインタは配列とポインタの関連に似ている。
配列は関数の引数になるとポインタに変わる。下の2つの関数aとbは同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <typeinfo> void a(char *p) { std::cout << typeid (p).name() << std::endl; } void b(char p[]) { std::cout << typeid (p).name() << std::endl; } int main() { char sz[10]; a(sz); b(sz); char* p = sz; a(p); b(p); } |
このように配列がポインタに変わる動作をdecayと呼ぶ。
下の2つの関数aとbも同くdecayする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <typeinfo> void a(char(*func)()) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } void b(char func()) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } char func() { return 'a'; } int main() { a(func); b(func); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> #include <typeinfo> void a(char *&p) { std::cout << typeid (p).name() << std::endl; } void b(char (&p)[10]) { std::cout << typeid (p).name() << std::endl; } int main() { char sz[10]; // error C2664: 'void a(char *&)': cannot convert argument 1 from 'char [10]' to 'char *&' (vc) // error: cannot bind non-const lvalue reference of type 'char*&' to an rvalue of type 'char*' (gcc) // a(sz); b(sz); char* p = sz; a(p); // error C2664: 'void b(char (&)[10])': cannot convert argument 1 from 'char *' to 'char (&)[10]' (vc) // error: invalid initialization of reference of type 'char (&)[10]' from expression of type 'char*' (gcc) //b(p); } |
関数bは配列のサイズも合ってないとエラーになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <typeinfo> typedef char (*FUNCP)(); void a(FUNCP &func) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } typedef char FUNC(); void b(FUNC& func) { std::cout << typeid (func).name() << std::endl; std::cout << func() << std::endl; } char func() { return 'a'; } int main() { // error C2664: 'void a(FUNCP &)': cannot convert argument 1 from 'char (__cdecl *)(void)' to 'FUNCP &' (vc) // error: cannot bind non-const lvalue reference of type 'char (*&)()' to an rvalue of type 'char (*)()' (gcc) // a(func); FUNCP myfuncp=func; a(myfuncp); b(func); // error C2072: 'myfunc': initialization of a function (vc) // error: function 'char myfunc()' is initialized like a variable (gcc) // FUNC myfunc=func; } |
値渡しはdecayするが参照渡しはdecayしない。これにtemplateが絡むとすごく複雑になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#include <Windows.h> #include <iostream> #include <memory> #pragma comment(lib, "Shell32.lib") using namespace std; class MyClass { public: MyClass() { cout << "ctor" << endl; } ~MyClass() { cout << "dtor" << endl; } }; int main() { { // basic unique_ptr<MyClass> p1(new MyClass); unique_ptr<MyClass> p2 = make_unique<MyClass>(); } { int* pI = new int(); unique_ptr<int> up(pI); } // array { unique_ptr<char[]> p(new char[100]); strcpy(p.get(), "Hello World!"); cout << p << endl; } // malloc and free { unique_ptr<char, void (__cdecl*)(void*)> p(static_cast<char*>(malloc(100)), free); strcpy(p.get(), "malloc world!"); cout << p << endl; } // malloc and free 2 { unique_ptr<wchar_t, std::function<void(void*)>> p(_tcsdup(L"abc"), free); cout << p << endl; } // C file { unique_ptr<FILE, int(__cdecl*)(FILE*)> file(fopen("myfile.txt","r"),fclose); // do something } // Win32 { int argc; unique_ptr<LPWSTR, HLOCAL(WINAPI *)(HLOCAL)> arg(::CommandLineToArgvW(L"aaa.exe a b \"c d e f\"", &argc), ::LocalFree); for(int i=0 ; i < argc ;++i) { wcout << i << L":" << arg.get()[i] << endl; } } return 0; } |
unique_ptrは渡されたポインタの所有権を保持する。つまりこのポインタを解放するのはunique_ptrの役割である。ポインタが破棄されるのはunique_ptrが破棄される時かreset()によって他のポインタがセットされる時である。よってget()で取得したポインタを破棄したり、他のスマートポインタに渡してはいけない。
unique_ptrをunique_prtにコピーというか移動するにはstd::moveを使わなくてはならない。
unique_ptrの第2テンプレート引数にはデリーターを指定する。この関数は破棄される際にポインタが渡される。デフォルトはdeleteなので作成時にnewで作ったオブジェクトを渡す。しかしnewで作れないオブジェクトはたくさんあるのでコード例のようにカスタムデリーターを指定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ unique_ptr<MyClass> p = make_unique<MyClass>(); unique_ptr<MyClass> q(std::move(p)); std::cout << "p = " << (p ? "not null" : "null") << std::endl; } std::cout << "======================" << std::endl; { unique_ptr<MyClass> p; p.reset(new MyClass); // OK, previous instance will be freed p.reset(new MyClass); } |
カスタムデリーターはポインタ1つを引数に取る関数オブジェクトのを指定しなければならない。呼び出し規約がデフォルトと違う場合はそれも指定しないといけないのでややこしい。この場合はstd::functionを使えば呼び出し規約を気にしなくてもいい。
開放関数が引数を2つ取るなどの場合はラムダやファンクタを指定することになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include <Windows.h> #include <iostream> #include <memory> #include <functional> #pragma comment(lib, "Shell32.lib") #pragma comment(lib, "user32.lib") using namespace std; class MyClass { public: MyClass() { cout << "ctor" << endl; } ~MyClass() { cout << "dtor" << endl; } }; int main() { // malloc and free { unique_ptr<char, std::function<void(void*)>> p(static_cast<char*>(malloc(100)), std::function<void(void*)>(free)); strcpy(p.get(), "malloc world!"); cout << p.get() << endl; } // C file { unique_ptr<FILE, std::function<int(FILE*)>> file(fopen("myfile.txt","r"),std::function<int(FILE*)>(fclose)); // do something } // Win32 { int argc; unique_ptr<LPWSTR, std::function<HLOCAL(HLOCAL)>> arg(::CommandLineToArgvW(L"aaa.exe a b \"c d e f\"", &argc), std::function<HLOCAL(HLOCAL)>(::LocalFree)); for(int i=0 ; i < argc ;++i) { wcout << i << L":" << arg.get()[i] << endl; } } // lambda { // OpenClipboard returns BOOL // Treat BOOL as void* unique_ptr<void, std::function<void(void*)>> clip(reinterpret_cast<void*>(::OpenClipboard(nullptr)), std::function<void(void*)>([](void*p) { if(p) CloseClipboard(); } ) ); } return 0; } |