C++について
似⿃啓吾

背景（なんでこんな話をするか）• 最近の計算機センター事情• ⾔語としてはFortran/C++の実質2択• プラス並列化としてMPIとOpenMP• 数値計算業界ではやはりFortranのシェアが⼤きい• FDPS (Framework for Developing Particle Simulators)• C++で書かれている• ユーザーコードもC++で書くことを当⾯は想定• ユーザーにC++の⾼度な知識は要求しない• MPIとOpenMPによる並列化はFDPS側で⾯倒を⾒る• が、C++の最低限の書き⽅は知っておく必要がある

習得しておきたいC++の機能• クラス（構造体） with メンバ関数• 「継承」や「仮想関数」といったオブジェクト指向機能はFDPSを使う分には不要• テンプレート• ⽤意されたものを使うことができれば⼗分• FDPS提供はクラステンプレートを提供、ユーザーはこれを実体化• メタプログラミングとかはユーザー側では不要• 標準ライブラリSTL• とりあえずstd::vectorを可変⻑配列として使えるぐらいで⼗分• あとは簡単なIO（C⾔語のものを使ってもいい）• C++の全ての⾔語機能を使おうとはしないこと• どんな上級者でも迷宮⼊りします教科書とか• 何が「良書」かというのは⾮常に難しい• ⾔語機能が多すぎてコーディングスタイルが多様• C⾔語を経由してC++を知りたいひとには以下が名著

⼀緒にFDPSを開発したい⼈向け• ユーザーには不要です• FDPSの実装にはとても役⽴ちました• 興味本位で閲覧してみるのも、、、

⾔語のバージョン等• 多くのプログラミング⾔語にはバージョンがあります• FORTRAN/Fortranだと66, 77, 90, 95, 2003, 2008等• C++には98, 03, 11, 14• 便利な新機能が増えることもありますが、新しすぎるとコンパイラの対応が追いつかないことも• FDPSはC++03で実装してあります• 「京」でC++11はまだのようなので• ユーザーコードはC++11/14で、というのも可能です• 「京」での使⽤も考えている⽅は03に留めるのが無難です• GNU、Fujitsu、Clangぐらいの環境ではサンプルコードが通ることを確認するようにしていますが、中でテンプレートを多⽤しているので環境やバージョンによっては問題が発⽣するかもしれません。
• 環境やバージョン等でコンパイルに問題が発⽣したときはサポートまでご⼀報お願いしますクラス（構造体）• C⾔語の構造体• struct Vector3{
double x, y, z;};• データをパックして新しい型を作る• 複数の型を混在させることもできる• C++での拡張• struct Vector3{
double x, y, z;
double norm() const {return sqrt(x*x + y*y + z*z);}};• 「メンバ変数」に加えて「メンバ関数」も書けるようになった• C++でのclassとstructは実質的に同じもの

クラス（構造体）の使⽤例
template <typename T>
struct Vector3{T x, y, z;

空間ベクトル型と演算⼦の定義

const Vector3 &operator+=(const Vector3 &rhs){
x += rhs.x; y += rhs.y; z += rhs.z;
ここはFDPS側が提供
return (*this);}friend Vector3 operator*(const T &s, const Vector3 &v){
Vector3 t = {s*v.x, s*v.y, s*v.z};
return t;}};
typedef Vector3<double> F64vec;
struct Particle{
F64vec pos, vel, acc;
void kick(const double dt){
vel += dt * acc;}
void drift(const double dt){pos += dt * vel;}

ユーザー定義の粒⼦構造体
メンバ関数で数値積分};
void integrate(Particle &p, const double dt){
p.kick (dt);p.drift(dt);}メンバ関数をグローバル関数
から呼び出してみるテスト

コンストラクタ／デストラクタと変数の寿命#include <iostream>

特別なメンバ関数：• コンストラクタ：変数の⽣成時に呼ばれる• デストラクタ ：変数の消滅時に呼ばれる

struct Life{
const char *name;
Life(const char *_name) : name(_name){
std::cout << "begin " << name << std::endl;}~Life(){
std::cout << "end
" << name << std::endl;}};
static Life global("global");int main(){Life("immediate");Life outer ("outer");{Life inner("inner");}
return 0;}コンストラクタで⽂字列を表⽰
デストラクタで⽂字列を表⽰

実行結果
begin global
begin immediateend
immediate
begin outer
begin inner
end
inner
end
outer
end
globalテンプレート (template)• クラスまたは関数の「雛形」• 「クラステンプレート」と「関数テンプレート」とある• テンプレート名 <型名>のように<>で「テンプレート引数」を渡すことで、「実体化」したクラスや関数が得られる。

• FDPSでは以下のように変数宣⾔する• PS::ParticleSystem<FPGrav> system_grav;• PS::TreeForForceLong<FPGrav, FPGrav, FPGrav>::Monopole tree_grav;• 色付き文字で書かれた部分が変数名• クラスの中にはメンバ変数、メンバ関数だけでなく「メンバ形名」も持てる、::Monopoleがその例

STL (Standard Template Library)• とりあえずiostream（⼊出⼒）とvector（可変⻑配列）ぐらいを押さえておけば⼤概のことができる• ⼊出⼒に関してはC⾔語のもの(cstdio)を使ってもいい（FDPSを使う分にはどちらでも可能）#include <vector>
int main(){
std::vector<int> array;
array.resize(10);for(int i=0; i<10; i++){array[i] = i;}return 0;}参照渡し• C⾔語では関数に渡した値を書き換えて欲しいときはポインタを渡す必要があった• C++では参照渡しという機能が追加された、FORTRANに近い書き⽅になった• 配列もvectorの(constな)参照で渡せばいいので、ポインタ渡しを使う機会は少ない// ポインタ版
void copy(double *dst, const double *src){*dst = *src;}// 参照版
void copy(double &dst, const double &src){
dst = src;}
int main(){const double src=0.0;
double dst;
copy(&dst, &src); // ポインタ版
copy(dst, src);// 参照版
return 0;}

まとめ• FDPSを利⽤するにあたって抑えておきたいC++の機能• メンバ関数を持ったクラス（構造体）• 粒⼦クラス（構造体）はユーザー定義• FDPS提供クラスのメンバ関数をユーザーが呼び出す• テンプレート• ⽤意されたものを使うことができれば⼗分• FDPS提供のクラステンプレートに<>でユーザー定義の粒⼦クラス（構造体）を渡して実体化• 標準ライブラリ（STL）• IOはCのものかC++のもの（あるいは混在）• std::vectorを可変⻑配列として使えるぐらいで⼗分• C++の全ての⾔語機能を使おうとはしないこと