Androidでc++と連携してOpenCVを動かすサンプルプロジェクト解説

wrongwrong163377.hatenablog.com
github.com
こちらの記事で紹介したサンプルプロジェクトの解説記事です。
このプログラムは、以下のページを参考に作成しました。
OpenCV for Android入門 – カメラ編 « Rest Term

プログラムの概要

※このアプリではPermission関連のコードを含めていないため、アプリの実行にはAndroidの設定からカメラのパーミッションを有効にする必要が有ります

このプログラムでは、以下のことをやっています。カッコ内はMainActivity.java内での行数です。

  1. カメラから画像を取り込む(61~)
  2. 取り込んだ画像をrgba形式のMatに変換する(65)
  3. Matをc++のコードに渡し、typeをStringとして受け取り出力(70)
  4. Matをc++のコードに渡し、rgbaの内rを消去(71)
  5. 操作し終わった画像を画面に表示(73)

解説

MatTest1、MatTest2で共通しているのは、longで受け取ったNativeObjAddrからMatを操作している部分です。

Mat &input = *(Mat *) inputAddr;

後はメンバ関数を普通に呼び出しているだけですね。

cv::Mat::type

Mat::typeで帰ってくる値の読み方は以下のページを参考にしました。
ninghang.blogspot.jp
プログラム内のコメントでも少し触れた通り、幾つかの環境で試した結果、MainActivityの65行目のようにrgbaで取得した場合、帰ってくる値は24(CV8UC4)となるようです。
66行目のように、grayでも受け取ることができますが、この場合帰ってくる値は0(CV8UC1)でした。

cv::Mat::forEach

しっかりと理解して書いた訳ではないのですが、ここで利用しているforEach文は、全画素に対してラムダ式の内容を勝手に並列実行してくれるらしいです。

AndroidでOpenCV + NDKでc++と連携してOpenCVを動かす

タイトル通り、AndroidOpenCVを導入し、c++と連携してOpenCVを動かす所まで書きます。
実現する状況は以下の通りです。

最後は個人的な都合でやってます。
OpenCV4Android SDKOpenCV Managerを介して利用することもできますが、今回はポータビリティに重点を置き、プロジェクトの共有ライブラリに.apkを含める形で進めます。

完全にネット上の知識のみの独学でやっているので、間違っている点やおかしな点があるかもしれません。
特にファイルの配置に関しては色々と無駄なことをやっていると思います。
アドバイスやコメント大歓迎です。

この記事の内容と、多少のサンプルコードを加えたプロジェクトをGitHub上で公開しています。
github.com

実行環境

環境は以下の通りです。

AndroidStudio 2.3.3
OpenCV4Android SDK 3.2.0
Gradle version 3.3
NDK version b06

プロジェクトの作成

今回の記事で実行する手順です。

  • (c++のコードを導入するよう設定しプロジェクトを開始)
  • OpenCV4Android SDKの導入
  • c++OpenCVを触れるように設定

プロジェクトの開始

今回はc++との連携を行うため、プロジェクト開始時にInclude C++ supportにチェックを入れます。f:id:wrongwrongwrongwrong163377:20170701165649p:plain
ついでにc++11を有効に。
f:id:wrongwrongwrongwrong163377:20170701165847p:plain
後は適当に選んでプロジェクトを開始します。

NDKのインストール

写真がありませんが、Include C++ supportにチェックを入れてプロジェクトを開始した場合、NDKのインストールが必要になります。
これはAndroidStudioからのメッセージに従ってインストールしていけば何も問題ありません。
以下の画面までたどり着いたら完了です。
f:id:wrongwrongwrongwrong163377:20170701165850p:plain

OpenCV4Android SDKの導入

qiita.com
この項目は、こちらの記事を参考に作成しました。

ダウンロード・解凍・配置

以下のページからバージョンを選びSDKをダウンロードしてきます。
sourceforge.net
今回はopencv-3.2.0-android-sdk.zipをダウンロードしてきました。
zipファイルは解凍したあとでできるだけ動かさない場所に置くと良いでしょう。

プロジェクトへモジュールを導入

AndroidStudioで、File > new > Import Moduleを選択します。f:id:wrongwrongwrongwrong163377:20170701181452p:plain
以下の画面に、「[解凍したものを配置した場所]/OpenCV-android-sdk/sdk/java」を入力し、ライブラリの追加を行います。
f:id:wrongwrongwrongwrong163377:20170701181631p:plain
この画面の後にももうひとつ画面が出ますが、特に弄る必要はありません。
実行後には、[プロジェクトディレクトリ]/import-summary.txtが開きます。
内容(抜粋)は以下のようになります。

ECLIPSE ANDROID PROJECT IMPORT SUMMARY
======================================

Ignored Files:
--------------
The following files were *not* copied into the new Gradle project; you
should evaluate whether these are still needed in your project and if
so manually move them:

* javadoc/
* javadoc/allclasses-frame.html
* javadoc/allclasses-noframe.html
* javadoc/constant-values.html
* javadoc/help-doc.html
* javadoc/index-all.html
* javadoc/index.html
* javadoc/org/
* javadoc/org/opencv/
* javadoc/org/opencv/android/
* javadoc/org/opencv/android/BaseLoaderCallback.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewFrame.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener2.html
︙

インポートしたものが確認できます。

ライブラリをプロジェクトに追加

プロジェクトのapp/src/mainに、jniLibsという名前のディレクトリを作成し、[解凍したものを配置した場所]/sdk/native/libs内のファイルを全てコピーペーストします。
以下のようになります(※opencv320というディレクトリが追加されているのは一旦無視して下さい)。
f:id:wrongwrongwrongwrong163377:20170701182926p:plain

モジュールの依存関係を設定

AndroidStudioで、File > Project Structureからappを選択し、Dependenciesタブの緑の'+'マークからModule depencencyを選択します。
f:id:wrongwrongwrongwrong163377:20170701183802p:plain
f:id:wrongwrongwrongwrong163377:20170701183808p:plain
すると先ほど取り込んだモジュールが表示されるので、それを選択しOKを押します。
f:id:wrongwrongwrongwrong163377:20170701183827p:plain
以下のようになります。f:id:wrongwrongwrongwrong163377:20170701183950p:plain

gradleの設定

素の状態では、OpenCVコンパイルに関する設定のAPIレベル等が低い状態です。
例えばcamera2 APIを利用しようと思った場合、これはAPIレベル21以降の実装であるため、build.gradleの設定無しではビルドができなくなります。
これを解消するため、build.gradleの設定を変更します。

app/build.gradleと、openCVLibrary320/build.gradleを開きます(320はバージョン名)。
f:id:wrongwrongwrongwrong163377:20170701184419p:plain
編集前の内容は、それぞれ以下のように(抜粋)なっています。

app/build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1

openCVLibrary320/build.gradle(編集前)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 14
    buildToolsVersion "25.0.0"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 21
    }
︙

このうちopenCVLibrary320/build.gradleのcompileSdkVersion, buildToolsVersion, minSdkVersion, targetSdkVersionの4項目を、app/build.gradleに合わせて書き換えます。

openCVLibrary320/build.gradle(編集後)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"

    defaultConfig {
        minSdkVersion 22
        targetSdkVersion 25
    }
︙

ここまでの設定で、JavaからOpenCVを利用することが可能になります。

c++からOpenCVを利用する

次に、Androidc++からOpenCVを利用するための設定を行います。
正直この辺りのことは全く理解できていません。
以下のリポジトリから、設定をコピペする形で設定を行いました。
github.com

([解凍したものを配置した場所]/OpenCV-android-sdk/sdkの配置)

持ち運びのためにやっていることなので、やらなくても良い手順です。
app直下にopencv320という名前のフォルダを作成し、[解凍したものを配置した場所]/OpenCV-android-sdk/sdkをコピペします。

app/build.gradleの設定

app/build.gradleに設定を追加します。

app/build.gradle(編集前)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
︙

追加した内容は、cmake内と、buildTypesの手前の内容です。

追加した部分1

cmake {
    cppFlags "-std=c++11"
    abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
}

追加した部分2

sourceSets {
    main {
        jniLibs.srcDirs = ['src/main/jniLibs']
    }
}

app/build.gradle(編集後)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
                abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
            }
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
︙
app/CMakeLists.txtの設定

初期状態CMakeLists.txtは以下の通りです(最初から入ってるコメントは邪魔なので消しました)。
app/CMakeLists.txt(編集前)

cmake_minimum_required(VERSION 3.4.1)

add_library( native-lib SHARED src/main/cpp/native-lib.cpp )

find_library( log-lib log )

target_link_libraries( native-lib ${log-lib} )

これを以下のように編集します

app/CMakeLists.txt(編集後)

cmake_minimum_required(VERSION 3.4.1)

# 以下3行追加
include_directories(./opencv320/sdk/native/jni/include) #置いた場所で変わる
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

add_library( native-lib SHARED src/main/cpp/native-lib.cpp )

find_library( log-lib log )

# lib_opencv追加
target_link_libraries( native-lib lib_opencv ${log-lib} )

注意点として、include_directories(./opencv320/sdk/native/jni/include)の部分では、自分がsdk/jni/includeを置いた場所を指定する必要があります。
ここまでの設定で、Androidc++からOpenCVを利用することができるようになります。

疑問

以下の点が疑問として残っていますので、追々調べていきます。

  • 例えばg++などに渡す最適化のフラグはどのようにして渡すのか(そもそも渡せるのか)
  • 無駄な手順が含まれていないか(全部やってから動くことは確認したが、途中でどう動かないか記録していない)

おまけ

掲載したサンプルプロジェクトの解説を書きました。
wrongwrong163377.hatenablog.com

c++で、if文と%(剰余)のどちらが速いかを少しだけ比較

ある一定数で折り返して用いたいような数があった場合、ぱっと思いつくのは、if文を用いて初期化するやり方と、%で剰余を取る方法でしょうか。
例えば以下のようなコードです。

//if文を用いたコード
while(i < MAX) {
    if((j+=1) == 100) j = 0;
    i++;
}

//%を用いたコード
while(i < MAX){
    j = (j+1)%100;
    i++;
}

どちらのコードでも、jが100になったら0に戻す、という処理をしています。
このif文を用いて初期化する方法と、%で剰余を取るコード、どちらが高速なのか気になったので、検証を行ってみました。

実行環境

実行環境は以下の通りです。

OS Windows10 Pro
CPU Core i7 6700
MEM DDR4 2133CL13 8GBx2
コンパイラ(コンパイルオプション) g++5.3.0-3(-Wall -O3)

※CLion上のRunから実行しました
c++11でコンパイルしました

実行したコード

プロジェクト全体はGitHubに上げました。
github.com

#include <iostream>
#include <chrono>

using namespace std;

constexpr int MAX = 1000000000;

int If(){
    int i = 0;
    int j = 0;

    chrono::system_clock::time_point start, end;
    start = chrono::system_clock::now();
    while(i < MAX) {
        if((j+=1) == 100) j = 0;
        i++;
    }
    end = chrono::system_clock::now();

    cout << "if time\t\t:" << chrono::duration_cast<chrono::milliseconds>(end-start).count() <<endl;

    return i + j;
}

int Mod(){
    int i = 0;
    int j = 0;

    chrono::system_clock::time_point start, end;
    start = chrono::system_clock::now();
    while(i < MAX){
        j = (j+1)%100;
        i++;
    }
    end = chrono::system_clock::now();

    cout << "mod time\t:" << chrono::duration_cast<chrono::milliseconds>(end-start).count() <<endl;

    return i + j;
}

int main() {
    int Ifres, Modres;
    Ifres = If();
    Modres = Mod();

    cout << "if result\t:" << Ifres << '\n'
         << "mod result\t:" << Modres << endl;
    return 0;
}

実行結果

実行結果は以下の通りです。
10回ほど実行して最速だったものを選びました。

Mod If
3088[ms] 1403[ms]

結論

検証はガバガバですが、一目でわかる通りif文方が倍以上高速ですね。
大体の平均では実行時間は半分程度でしたが、iの加算処理が等しく入っていることを考えると、倍以上速いという結論は揺らがないでしょう。
あまり差が出なかったらちゃんと検証するためのコードを書くつもりでしたが、その必要も無かったです。
実験前は、加算と保存の後で更に処理を行うということで、if文を用いた処理の方が遅いのかなと思っていましたが、最適化レベルなどを弄ってみても、やはりif文を用いた方が速かったです。
恐らくif文の方が%演算子よりも分岐予測がしっかりしているためなのでしょうね。

おまけ

javaでもやってみました。
wrongwrong163377.hatenablog.com

参考にしたサイト

時間計測はこちらのサイトを参考にさせて頂きました。
qiita.com

CMakeでコンパイルオプションを複数指定する

set(CMAKE_C_FLAGS "-Wall -O3") #cの場合
set(CMAKE_CXX_FLAGS "-Wall -O3") #c++の場合

というように、指定したいオプションをスペースで区切る。
cとc++で違いがあるようなので注意。

参考にしたページ

cmake の使い方 - PukiWiki

自分を知るためのツール

個人の資質だとかについて、思っていることを書きます。

たくさんの「自分を知るためのツール」

今の時代、有料から無料から、「自分を知るためのツール」というものがたくさんあります。

これらは、個人の資質や性格を、かなり定量的に評価できる段階まで進歩しています。

個々の診断に関しては触れませんが、自分がやったことのある診断と結果を幾つか挙げます。

ストレングス・ファインダー
さあ、才能(じぶん)に目覚めよう―あなたの5つの強みを見出し、活かす

さあ、才能(じぶん)に目覚めよう―あなたの5つの強みを見出し、活かす

 

 (※自分が受けたのは旧版の診断です、現在ストレングス・ファインダー2.0が売り出し中です)

  1. 戦略性
  2. 着想
  3. 内省
  4. 適応性
  5. 原点思考
 医師のつくった「頭のよさ」テスト
医師のつくった「頭のよさ」テスト 認知特性から見た6つのパターン (光文社新書)

医師のつくった「頭のよさ」テスト 認知特性から見た6つのパターン (光文社新書)

 

 (※この診断については別記事で詳しく書いています読書感想「医師の作った「頭のよさ」テスト」 - wrongwrongな開発日記)

  1. <視覚優位者>カメラアイタイプ:25
  2. <視覚優位者>三次元映像タイプ:18
  3. <言語優位者>言語映像タイプ:29
  4. <言語優位者>言語抽象タイプ:13
  5. <聴覚優位者>聴覚言語タイプ:20
  6. <聴覚優位者>聴覚&音タイプ:15
エムグラム診断

check.m-gram.jp

16Personaslities

個人の可能性は無限でも何でもない

こういった診断を受けて思うことは、個人の可能性なんて無限でもなんでもないということです。特に質問数が50問を越えるような、しっかりと練られた性格診断は、自分の性格を的確に言い当ててきます。

勿論、個々の診断で見える部分、見えない部分があるのは確かです。個々人の細やかな差異によって、「こんな性格・適性を持つ個人」という可能性は無限でしょう。

しかし、人間は人間であるという範囲から出ることができない、自分という範囲から出ることができないということも、重要な事実だと自分は思います。

自己責任はそんなに万能な言葉じゃない

人の性格・適性は、残念ながら偏っていて、できるできないがあるのは当たり前の話だと自分は思っています。

その中で非常に気になるのが、「誰だってこれができるのが当たり前」という前提で発せられる自己責任という言葉です。

確かに自助努力・自己責任という考え方はあって然るべきものですが、それだけで全てを説明することは、個性や多様性の否定ではないでしょうか。

自分や他人を知ることは、欠点を受け入れるための準備だ

自分は、自分を知るためのツールを使うことは、自分や他人の欠点を受け入れるための準備になると思っています。

個人には欠けている所があって、一方で長所もあります。

しかし、人間はどうしても、一か所欠けているからすべてが駄目だとか、一か所良いのだからすべて良い必要が有るだとか、個性というものを極端に一般化しようとするような部分があるように、自分は感じています。

自分を知るためのツールは、定量的に自分自身の個性を把握させてくれます。そして、「こういう他人も居る」ということも教えてくれます。

 重要なのは、それを知っていることなのだろうと、自分は思います。知っているからこそ、自分や他人の欠点を受け入れられるのだと思います。

まとめ

長々と自分の考えを書きましたが、ここで主張したいのは、具体的にどうとは言えないけど、自分を知るためのツールを知って、使うというのはとてもいいことだということです。

こういった話をできる友人は残念ながら少ないので、まだまだこういったツールの認知度は低いのでしょう。

個人的には、世間で一般化される位に流行ってくれるといいなと思っています。

ぼくのかんがえた最高の自作PC2017年6月版

最近忙しくてブログの更新や、好きなプログラムを組む時間が取れていない感じですが、息抜きに「ぼくのかんがえた最高の自作PC」を考えてみたので、記事にしちゃうことにします。

ここに書いた内容は価格.comのピックアップリストにも書きましたが、どうしてもはてなで書いた方が見栄が良く文字数も多く書くことができるので、こっちでも書いてみることにします。

kakaku.com

コンセプト

紹介する製品のコンセプトは、コンパクト・高性能・良ビジュアルです。

ケース "SST-SG13B"

kakaku.com

SilverStoneのITX専用ケースです。

この製品のここがスゴイ!

自分がこのケースを気に入っている理由は、簡易水冷GPUを組み込むことができる最小の(自分調べ)ITXケースである点です。

他にも小さいITX用PCケースというと、DAN Case A4-SFX v2や、NCASE M1 Version.5などがありますが、これらのケースはCPUの冷却が若干不安だったり、簡易水冷GPUを組み込めないといった点で自分の求めるスペックとは違います。

簡易水冷GPU以外にも、SG13とSST-PP08・SFX電源を合わせることで、推奨スペック的には71mmまでの高さのクーラーが入ります。実際にはもう少し背の高いクーラーも入るので、CPU冷却という点でも良好なスペックを得られます。

ビジュアルの点では、メッシュから漏れ出る光、というのがカッコいいのではないかなあと思っています。

以上のように、コンパクト・高性能・良ビジュアルを両立できるという意味でこのケースを選びました。

個人的には、対応電源をSFX規格のみにし、搭載できるCPUクーラーの高さを増したSG13改のようなものを期待しています。

マザーボード "ASUS ROG Strix Z270I Gaming"

www.asus.com

 ASUSのITXマザーです。

この製品のここがスゴイ!

LED付きパーツの流行と、個人的にお勧めな自作PCパーツの紹介 - wrongwrongな開発日記

上の記事で以前書きましたが、この製品のスゴイ点は、Mini-ITXマザーながらM.2スロットを2本揃え、M.2スロット用のヒートシンクを1つ装備し、ライティングLEDを装備している点です。

これだけ欲張って盛ったマザーボードを自分は知りません。

ただ、GPUやメモリなど、パーツのRGB LED制御の兼ね合いで、Gigabyte製マザーに浮気する可能性もあるかなーと感じています。

何にせよ、RGB LED制御は、メーカーを横断して使えるように早く規格を定めてほしいですね。

CPUクーラー "Cryorig C1(Cu Series)"

kakaku.com

Cryorigのプレスリリース:CRYORIG COMPUTEX2015 | Research Idea Gear

こちらはCOMPUTEX 2017で発表された全銅ヒートシンクを採用したシリーズで、まだ未発売のものですが、冷却性能を重視して採用しました。

この製品のここがスゴイ!

Cryorig C1は、SG13に搭載可能なCPUクーラーの中で最高の性能を誇っています。

まだ未発売のCu Seriesですが、冷却力は確実に伸びているでしょう。

[元のクーラー許容高] + ([ATX電源高さ] - ([SFX電源高さ] + [PP08の上側の高さ])) = [許容できるCPUクーラー高さ]で、ざっくり出した値で計算すると61 + (86-(64+5)) = 78mmであり、また以下のようにbe quiet! SHADOW ROCK LPを搭載している情報もあるので、搭載できるのはほぼ確実です。

Tiny Silverstone SG13 build - Build Logs - Linus Tech Tips

 メモリー "(Kingston HyperX Predator LED)"

www.youtube.com

CES 2017にて発表された、KingstonのRGB対応メモリーです。

この製品のここがスゴイ!

この製品の特長は、未確認ながらヒートシンクの全高が現状RGB対応メモリーの中で最低である点です。

HyperX Predatorとヒートシンク高に変わりが無い場合、唯一Cryorig C1と組み合わせることのできるRGB対応メモリとなります。

ソース:機能てんこ盛り「ASRock Fatal1ty Z270 Gaming-ITX/ac」レビュー。Mini-ITXながらオールラウンドに対応可能、TB3接続で外付けグラフィックボードもサポート : 自作とゲームと趣味の日々

……メモリーが隠れるトップフロークーラーでメモリー光らせてどうすんだってのはありますが、ヒートシンクの隙間から光が見えるのはカッコいいと思うんですよね。

GPU "(Gigabyte GV-N1080TXTREME W-11GD)"

www.gigabyte.jp

GigabyteのAIO簡易水冷GPUです。

この製品のここがスゴイ!

AIO簡易水冷GPUは、EVGAやMSIなど幾つか種類がありますが、GigabyteのWaterforceシリーズは、簡易水冷GPUの中では最高の性能を有しています。

また、Waterforceシリーズは、空冷とのHybrid構成を採用していない点も自分の好みに合っています。

SG13を採用した理由も、このGPUを積んだITXのコンパクトシステムを組みたいという理由があったからです。

GTX 1080Tiを採用したものはまだ発表すらされていませんが、発表が楽しみです。

PSU "SilverStone SST-SX800-LTI"

kakaku.com

SilverStoneの800W Titanium SFX電源です。

この製品のここがスゴイ!

現状、SFX電源の中で最高の電源です。

800Wの大容量かつ低発熱のTitanium対応製品はこれしかありません。

SFX電源かつガッチリOCを考える構成をするなら、まず間違いなく一択の電源です。

ファングリル "SilverStone SST-FG121" 

SilverStone RGB LED内蔵ファングリル 120mmファン用 SST-FG121 日本正規代理店品

SilverStone RGB LED内蔵ファングリル 120mmファン用 SST-FG121 日本正規代理店品

 

 SilverStoneのRGB対応ファングリルです。

この製品のここがスゴイ!

 ケースと同じくSilverStoneの製品です。

SG13の正面にこの製品を搭載すれば、メッシュにSilverStoneのロゴが浮かび上がり、最高にカッコよく見えるのではないかと妄想しています。

まとめ

採用したい最低限のパーツの紹介でしたが、いかがでしたでしょうか。

最近ではゲーミングノートやBTO品などを選んだ方が、パーツへの知識も必要無く、最低限動く保証のあるものが手に入ります。

そんな中でPC自作を選ぶ理由は、やはりパーツ選択の自由度や、PCに自分なりのテーマを持ち込めることでしょう。

財布に余裕があればもう既に組んでいるのでしょうが、組むのはもっと先になりそうです。

妄想だけでも十分楽しめますが、やはり実際に組みたいですね。

追記

続きました。

wrongwrong163377.hatenablog.com

C/C++/(Java)、見やすい関数の書き方

最近学んだ、CやC++Javaといった言語での見やすい関数の書き方を纏めます。
まず、以下の2つのコードをご覧ください。

int func(int a, int b, int c){

    processing...

    return result;
}
int
func(
        int a,
        int b,
        int c
){

    processing...

    return result;
}

ここで定義している関数2つは、どちらも全く同じ処理をしていますが、後者の方が良い書き方だと教えて頂きました。

何故かというと、極端な例として、以下のようなコードを書く際には、前者のコードでは横に長くなり過ぎてコードの見通しが悪くなるからです。
具体的には、戻り値と関数名、引数として何を取っているかが分かりにくいですね。

inline LongNameClass LongNameFunction(LongNameClass InputData1, LongNameClass InputData2, LongNameClass InputData3, LongNameClass InputData4){

    processing...

    return result;
}

この場合、後者の書き方の方が断然見やすいです。

inline LongNameClass
LongNameFunction(
        LongNameClass InputData1,
        LongNameClass InputData2,
        LongNameClass InputData3,
        LongNameClass InputData4
){

    processing...

    return result;
}

また、この書き方には、各引数の説明をコメントとして書きやすいというメリットもあります。

inline LongNameClass
LongNameFunction(
        LongNameClass InputData1, //コメント
        LongNameClass InputData2, //コメント
        LongNameClass InputData3, //コメント
        LongNameClass InputData4  //コメント
){

    processing...

    return result;
}

個人的な印象ですが、こういった書き方をする/した方がいいのは、特にC系の言語で関数を作成するような場面かなと思います。
Javaではとにかくクラスに入れてやり取りする場面が多く、引数が多くなりにくいですし、そもそも短いコードではこの書き方をすると若干冗長に見えますので、使い分けも大切かなあと。