[C++] ファイルの分割

C++におけるファイルの分割はクラス定義単位で行う

### ヘッダファイル
マクロ定義、型定義、プロトタイプ宣言の3つを書くのが基本
C++ではクラスを利用するには、そのクラスが提供するヘッダファイルを取り込む必要がある
以下のようなファイルがあったとする

class POS {
    private:
        int x;
        int y;
    
    public:
        POS();
        int setPos(int tmpx, int tmpy);
}

POS::POS(){
    x = 0;
    y = 0;
}

int POS::setPos(int tmpx, int tmpy){
    x = tmpx;
    y = tmpy;
    
    return 0;
}

これを分割する場合

POS.h

#ifndef POS_H
#define POS_H

class POS {
    private:
        int x;
        int y;
    
    public:
        POS();
        int setPos(int tmpx, int tmpy);
}

#endif

POS.cpp

#include "POS.h"

POS::POS(){
    x = 0;
    y = 0;
}

int POS::setPos(int tmpx, int tmpy){
    x = tmpx;
    y = tmpy;
    
    return 0;
}

つまり、最初にheader情報を見て、全体の流れを理解してからソースファイルを見ていくのね。※以前はcppを先に見て、.hは付加情報かと思っていました…orz

HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
std::vector& received_headers, const bool full_headers_message)
で、ProcessingResult ProcessNextHeaders となっているのは、headerssync.hで、ProcessingResultの構造体を示していたので、このProcessingResultの型で返却して欲しい という書き方なのね。

struct ProcessingResult {
        std::vector<CBlockHeader> pow_validated_headers;
        bool success{false};
        bool request_more{false};
    };

[bitcoin] headerssync-paramsって何?

Readmeを読むと以下のように書いている
A script to generate optimal parameters for the headerssync module (src/headerssync.cpp). It takes no command-line
options, as all its configuration is set at the top of the file. It runs many times faster inside PyPy. Invocation:
つまり、node間でのヘッダー情報のやり取りのことだ。c++ファイルのヘッダではない。stateで、 PRESYNC、REDOWNLOAD、FINALとある。

src/headerssync.h, src/headerssync.cpp を見てみる。
### headerssync.h
A compressed CBlockHeader, which leaves out the prevhash
HeadersSyncState
We wish to download a peer’s headers chain in a DoS-resistant way.
* – In the first download phase, called pre-synchronization, we can calculate
* the work on the chain as we go (just by checking the nBits value on each
* header, and validating the proof-of-work).
In phase 1 (presync)
In phase 2 (redownload)

#### headerssync.cpp
// The two constants below are computed using the simulation script in
// contrib/devtools/headerssync-params.py.
m_max_commitments = 6*(Ticks(NodeClock::now() – NodeSeconds{std::chrono::seconds{chain_start->GetMedianTimePast()}}) + MAX_FUTURE_BLOCK_TIME) / HEADER_COMMITMENT_PERIOD;

downloadはこれでやってる?

    if (m_current_chain_work >= m_minimum_required_work) {
        m_redownloaded_headers.clear();
        m_redownload_buffer_last_height = m_chain_start->nHeight;
        m_redownload_buffer_first_prev_hash = m_chain_start->GetBlockHash();
        m_redownload_buffer_last_hash = m_chain_start->GetBlockHash();
        m_redownload_chain_work = m_chain_start->nChainWork;
        m_download_state = State::REDOWNLOAD;
        LogPrint(BCLog::NET, "Initial headers sync transition with peer=%d: reached sufficient work at height=%i, redownloading from height=%i\n", m_id, m_current_height, m_redownload_buffer_last_height);
    }

downloadのstatusに応じて処理しているのはわかるが、getHashの処理とかはheaderssync.cppの中には書いてないように見えるがどこでやってるんだろうか?

あと、基本的に、HeadersSyncState::${処理}と書かれていますね。
なんか凄いな…

[C++] 循環参照

クラスの相互参照における問題
A.h

#ifndef _A_H_
#define _A_H_

#include "B.h"

class A {
    B* m_bB;
    ...
}

#endif // _A_H_

ifndef, define, endifは重複回避

B.h

#ifndef _B_H_
#define _B_H_

#include "B.h"

class B {
    A* m_bA;
    ...
}

#endif // _B_H_

お互いに参照していると、いつまで経っても参照が終わらなくなってしまう

#ifndef _A_H_
#define _A_H_

class B;

class A {
private:
    B* m_bB;
public:
    A();
    void foo();
    void bar();
    ...
}

#endif // _A_H_
#ifndef _B_H_
#define _B_H_

class A;

class B {
private: 
    A* m_bA;
public:
    B(A* pA);
    void hoge();
}

#endif // _B_H_

includeはしていないのだが、うーん、なんかよくわからんな…

[C++] for文のautoの書き方

std::vector<int> v;

for(std::vector<int>::const_iterator it = v.begin(), e=v.end(); it != e; ++it){
    std::cout << *it << std::endl;
}

c++11の範囲for文を使うと以下のように書ける

std::vector<int> v;

for(const auto& e: v){
    std::cout << e << std::endl;
}

### 範囲for文(range-based for statement)
配列やコンテナなど複数の要素を持つものから、全ての要素に含まれる値を取り出して処理する

#include <iostream>
using namespace std;

int main() {
    int a[] = {1, 2, 3, 4, 5};
    int sum = 0;
    for(int x : a){
        sum += x;
    }
    cout << "sum = " << sum << end;
    return 0;
}

vector要素の出力

    vector<string> v(5);
    for (int i = 0; i < v.size(); i++){
        string& x = v[i];
        cin >> x;
    }

autoは型推論を行うキーワード

    vector<string> v(5);
    for (auto& x : v){
        cin >> x;
    }

[C++] ヘッダとソースでファイルを分ける

C++ではクラスの定義とそのメンバ関数の定義をヘッダファイルとソースファイルで分割するのが一般的である

### ファイルを分割していない例
c.hpp

#ifndef C_HPP
#define C_HPP

class c {
    private:
    int m_value;

    public:
    int get() {
        return m_value;
    }

    void set(int const value){
        m_value = value;
    }
};

#endif

### ファイルを分割した例
c.hpp

#ifndef C_HPP
#define C_HPP

class c {
    private:
    int m_value;

    public:
    int get();

    void set(int const value);
};

#endif

c.cpp

#include "c.hpp"

int c::get(){
    return m_value;
}

void c::set(int const value){
    m_value = value;
}

インラインで書くこともある

#ifndef C_HPP
#define C_HPP

class c {
private:
int m_value;

public:
int get();

void set(int const value);
};

inline int c::get(){
return m_value;
}

inline void c::set(int const value){
m_value = value;
}

#endif

bitcoinのソースコードで、基本hppとcppのファイル構造になっていたが、謎が解けた。

[C++].hppとは

### .hppとは
ヘッダーファイルで.hpp、.hというファイル形式で保存
.hppはc++が入っている。.hはc言語だがc++でも使用できる

sample.hpp

#ifndef Sample_hpp
#define Sample_hpp

#include <iostream>

using namespace std;

class Sample {
    public:
        string name;
};

#endif

SRM464 ColorfulBoxesAndBalls

You are playing a game where you have numRed red boxes, numBlue blue boxes, numRed red balls, and numBlue blue balls.

You must place a single ball into each box. Each box is then scored as follows:
If the box is red and it contains a red ball, you get onlyRed points.
If the box is blue and it contains a blue ball, you get onlyBlue points.
In all other cases, you get bothColors points.
Your total score is the sum of the scores of all the boxes.

Return the maximum possible total score you can get.

#include <algorithm>
#include <limits.h>
using namespace std;

class ColorfulBoxesAndBalls {
    public:
        int getMaximum(int numRed, int numBlue, int onlyRed, int onlyBlue, int bothColors){
            int ans = INT_MIN;
            int change = min(numRed, numBlue);

            for(int i =0; i <= change; i++){
                int myscore = (numRed - i) * onlyRed
                    + (numBlue - i) * onlyBlue
                    + 2 * i * bothColors;
                ans = max(ans, myscore);
            }
            return ans;
        }
};

max値をfor文で回して、全パターンの最大値を検索するアルゴリズム
数が少ない方をchangeと定義するのがみそ

C++ 入門の入門1

わざとエラーを発生させる

#include <iostream>
using namespace std;

int main(){
    cout << "hello world\n"
}

void main(){
    cout << "hello world\n";
}

### コンパイルオプション
オブジェクトファイル -c
実行ファイル -o
静的ライブラリをリンク -L
ヘッダを追加 -l
警告 -W, -wall
機能有効 -std=c++11
最適化 -O

#### 変数

int main(){
    int a = 12345;
    int b = 98765;
    cout << a + b << endl;
    cout << a - b << endl;
}

intは32bit

#include <iostream>
#include <limits>
using namespace std;

int main(){
    cout << numeric_limits<int>::lowest() << ", " << numeric_limits<int>::max() << endl;
}

floatが32ビットだがdoubleは64bit

int main(){
    double a = 1.23;
    double b = 5.43;
    cout << a * b << endl;
    cout << a / b << endl;
}

[C++] ファイルの入出力

#include <iostream>
#include <fstream>
using namespace std;


int main() {
    
    ofstream fout("test0.txt");
    if(!fout){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }
    else
        cout << "ファイルをオープンしました。\n";

    fout.close();
    cout << "ファイルをクローズしました。\n";

    return 0;
}

ファイル出力

    fout << "Hello\n";
    fout << "Goodbye\n";
    fout << "ファイルに書き込みました。\n";

### 書式を設定してファイル出力

#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;


int main() {
    
    ofstream fout("test0.txt");
    if(!fout){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }
    else
        cout << "ファイルをオープンしました。\n";

    const int num = 5;
    int test[num];
    cout << num << "人の点数を入力してください。\n";
    for(int i=0; i<num; i++){
        cin >> test[i];
    }

    for(int j=0; j<num; j++){
        fout << "No." << j+1 << setw(5) << test[j] << '\n';
     }

    fout.close();

    return 0;
}

### ファイルから入力

#include <iostream>
#include <fstream>
using namespace std;


int main() {
    
    ifstream fin("test0.txt");
    if(!fin){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }

    char str1[16];
    char str2[16];
    fin >> str1 >> str2;
    cout << "ファイルに書き込まれている2つの文字列は\n";
    cout << str1 << "です。\n";
    cout << str2 << "です。\n";

    fin.close();

    return 0;
}
int main() {
    
    ifstream fin("test0.txt");
    if(!fin){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }

    const int num = 8;
    int test[num];
    for(int i=0; i<num; i++){
        fin >> test[i];
    }

    int max = test[0];
    int min = test[0];
    for(int j=0; j<num; j++){
        if(max < test[j])
            max = test[j];
        if(min > test[j])
            min = test[j];
        cout << "No." << j+1 << setw(5) << test[j] << "\n";
    }

    cout << "最高点は" << max << "です。\n";
    cout << "最低点は" << min << "です。\n";

    fin.close();

    return 0;
}

$ g++ -o sample sample.cpp && ./sample
No.1 80
No.2 68
No.3 22
No.4 33
No.5 56
No.6 78
No.7 33
No.8 56
最高点は80です。
最低点は22です。

### コマンドライン引数

#include <iostream>
#include <fstream>
using namespace std;


int main(int argc, char* argv[]) {

    if(argc != 2) {
        cout << "パラメータの数が異なります。\n";
        return 1;
    }
    
    ifstream fin(argv[1]);
    if(!fin){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }

    char ch;
    fin.get(ch);

    while(!fin.eof()){
        cout.put(ch);
        fin.get(ch);
    }

    fin.close();

    return 0;
}

### practice

int main() {

    for (int i=1; i<=30; i++){
        cout.width(3);
        cout.fill('-');
        cout << i;
        if(i % 5 == 0) {
            cout << "\n";
        }
    }

    return 0;
}

$ g++ -o sample sample.cpp && ./sample test0.txt
–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 <fstream>
#include <iomanip>
using namespace std;


int main(int argc, char* argv[]) {
    
    ifstream fin(argv[1]);
    if(!fin){
        cout << "ファイルをオープンできませんでした。\n";
        return 1;
    }

    const int num = 8;
    int test[num];
    for(int i=0; i<num; i++){
        fin >> test[i];
    }

    int max = test[0];
    int min = test[0];
    for(int j=0; j<num; j++){
        if(max < test[j])
            max = test[j];
        if(min > test[j])
            min = test[j];
        cout << "No." << j+1 << setw(5) << test[j] << "\n";
    }

    cout << "最高点は" << max << "です。\n";
    cout << "最低点は" << min << "です。\n";

    fin.close();

    return 0;
}

[C++] マニピュレータ

入出力の書式に関する定義をmanipulatorという

int main() {
    
    cout << "こんにちは!" << endl;
    cout << "さようなら!" << endl;

    return 0;
}

10進数以外の出力

int main() {
    
    cout << "10を10進数で表記すると" << dec << 10 << "です。\n";
    cout << "10を8進数で表記すると" << oct << 10 << "です。\n";
    cout << "12を8進数で表記すると" << 12 << "です。\n";
    cout << "10を16進数で表記すると" << hex << 10 << "です。\n";

    return 0;
}

$ g++ -o sample sample.cpp && ./sample
10を10進数で表記すると10です。
10を8進数で表記すると12です。
12を8進数で表記すると14です。
10を16進数で表記するとaです。

出力幅指定

int main() {
    
    for(int i=0; i<=10; i++){
        cout << setw(3) << i;
    }
    cout << '\n';

    return 0;
}