【ndnSIM】Interest・DataパケットをIPのパケットに詰め込んだり、元に戻したり

モチベーション

Information Centric Networking(ICN)のシミュレーションで、ルーティング周りの処理を弄ってトラフィック量の計測がしたい。一番研究が盛んなNDNをベースにして、ちゃんとInterest & Dataパケットは使いたいし、ndnSIM または ndn-cxx + NFD で実装したい。
しかし、ndnSIMにしろndn-cxx + NFDにしろアプリケーション層の研究開発向けっぽくて、ルーティング周りを弄ろうとすると内部実装書き換える必要あるっぽいので、技術力のない自分にはキツい。
仮にアプリケーション層でやりたいことを実装して、中間地点に設置したノードにインストールしても、元々のルーティング処理が良い感じに処理してしまうのでアプリケーション層まで届けられずに転送されてしまう。
そこで、Interest & DataパケットをIPパケットに詰め込んでソケットで送ってしまえばNDNのルーティング処理を通さないので,上位層の自前のコードで色々と処理出来て計測結果取れるよね、みたいな話(日本語が壊滅的に下手すぎてダメ)

どうするのか

ndn::Interestクラス・ndn::Dataクラス双方にwireEncodeというメソッドが存在する。これはNDNパケットをTLVフォーマットで表現したndn::Block型に変換して返してくれるという優れものである。
ここからさらにuint8_t型定数ポインタに変換してくれるwireというメソッドを使えば良い。ここまで変換すれば、Packet型にPayloadとしてぶち込むことが可能になる。

実際のコード (Interestパケットの場合)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 適当にInterestパケットを作成する
ndn::Interest interest(ndn::Name("/ndn.com"));
interest.setInterestLifetime(ndn::time::milliseconds(100000));
interest.setMustBeFresh(true);

// Block型に変換する
ndn::Block block = interest.wireEncode();

// Packetにぶち込む
ns3::Packet packet(block.wire(), (uint32_t)block.size());

// Packetからデータを取り出す
uint8_t *buffer = new uint8_t[packet.GetSize()];
packet.CopyData(buffer, packet.GetSize());

// Block型に復元する
ndn::Block block_restored(buffer, packet.GetSize ());

// Interestパケットに復元する
ndn::Interest interest_restored(block_restored);

// 以下の2行は出力が同じになります
std::cout << interest << std::endl;
std::cout << interest_restored << std::endl;

ndn::DataクラスでもwireEncodeメソッドがあるので、Dataパケットでも同様に出来ます。

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
// 適当にDataパケットを作成する
std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>();
data->setName(ndn::Name("/ndn.com/content01.html"));
data->setFreshnessPeriod(ndn::time::seconds(10));
static const std::string content = "hogehogeclub";
data->setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.size());
ndn::security::v2::KeyChain m_keyChain;
m_keyChain.sign(*data);

// Block型に変換する
ndn::Block block = data->wireEncode();

// Packetにぶち込む
ns3::Packet packet(block.wire(), (uint32_t)block.size());

// Packetからデータを取り出す
uint8_t *buffer = new uint8_t[packet.GetSize()];
packet.CopyData(buffer, packet.GetSize());

// Block型に復元する
ndn::Block block_restored(buffer, packet.GetSize ());

// Dataパケットに復元する
ndn::Data data_restored(block_restored);

// 以下の2行は出力が同じになります
std::cout << *data << std::endl;
std::cout << data_restored << std::endl;

keyChain.sign()をやっておかないとエラーが出るので注意!
この後はNS-3のSocketとか使って良い感じに指定したノードに送ってあげましょう。


ndn-cxxの実行ファイルの置き場所が変わってた件

例えば、ndn-cxxを利用して書いたhoge.cppというファイルをコンパイルして実行する場合、hoge.cppexamples/下に置いて

1
2
3
$ ./waf configure --with-examples #以前configureしてある方は省略
$ ./waf
$ ./build/exsamples/hoge

として実行していたものが、更新したら実行出来なくなっていて、
色々探した結果

1
$ ./examples/hoge

とやって実行するように仕様が変更されていた……
せめてREADMEには書いておいてくれよ…..


ndn-cxxで実装したアプリケーションをndnSIMに移植する

モチベーション

ndn-cxxで実装したアプリケーションを、ノード数を増やした環境で実験に使いたい
→ ndnSIMに移植したい!

前提条件

実験環境 : Ubuntu 17.10 LTS
対象読者 : Named Data Networking(NDN)の研究をしている日本人, C++がそこそこに書ける日本人

公式にはなんて書いてあるの?

http://ndnsim.net/current/guide-to-simulate-real-apps.html
こちらにndn-cxxで書いたアプリケーションをndnSIMに移植する際のサンプルコードが書いてあります。
このコードはndnSIM/ns-3/src/ndnSIM/examples/ndn-cxx-simple/下と、ndnSIM/ns-3/src/ndnSIM/examples/ndn-cxx-simple.cppに入っています。
(ちなみに、先日の更新前まで上記リンク先で説明されていたndnSIM/ns-3/src/ndnSIM/examples/ndn-custom-apps.cppは全く使い物になりません)

普段ndnSIMを利用する際には自分で作ったアプリをscratch/下に置いて、ndnSIM/ns-3/下で

1
2
$ ./waf
$ ./build/scratch/hoge

と実行すれば良い””はず””なのですが、上記コード群をそのままscratch/下に持って来て実行しても、何故かうまくいきません。
しかもこのコードはノードが1つであり、実際に実験する際には複数ノードを使う場合がほとんどであるので、複数ノードの場合はどうするのか分からないという問題もあります……
ということで、同じ点で困っている日本人研究者のために記事を書き残しておきます。

とりあえず2ノード間通信を移植してみる

https://named-data.net/doc/ndn-cxx/current/examples.html
にあるサンプルコードをndnSIMに移植してみます。

まずはTrival Consumerを移植してみます。移植の際には名前空間に注意すること!

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
#ifndef NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_CONSUMER_HPP
#define NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_CONSUMER_HPP

#include <ndn-cxx/face.hpp>
#include <ndn-cxx/interest.hpp>
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/util/scheduler.hpp>

#include <iostream>

namespace app {

class CustomConsumer
{
public:
CustomConsumer(ndn::KeyChain& keyChain)
: m_keyChain(keyChain)
, m_scheduler(m_face.getIoService())
{
}

void
run()
{
ndn::Interest interest(ndn::Name("/example/testApp/randomData"));
interest.setInterestLifetime(ndn::time::milliseconds(1000));
interest.setMustBeFresh(true);

m_face.expressInterest(interest,
std::bind([] { std::cout << "Hello!" << std::endl; }),
std::bind([] { std::cout << "NACK!" << std::endl; }),
std::bind([] { std::cout << "Bye!.." << std::endl; }));

std::cout << "Sending: " << interest << std::endl;

m_face.processEvents(); // ok (will not block and do nothing)
// m_faceConsumer.getIoService().run(); // will crash
}

private:
void
onData(const ndn::Interest& interest, const ndn::Data& data)
{
std::cout << data << std::endl;
}

void
onNack(const ndn::Interest& interest, const ndn::lp::Nack& nack)
{
std::cout << "received Nack with reason " << nack.getReason() << " for interest" << interest << std::endl;
}

void
onTimeout(const ndn::Interest& interest)
{
std::cout << "Timeout " << interest << std::endl;
}

private:
ndn::KeyChain& m_keyChain;
ndn::Face m_face;
ndn::Scheduler m_scheduler;
};

} // namespace app

#endif // NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_REAL_APP_HPP

Consumerの処理をシナリオコードからstartさせるためのコードも必要になります。

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
#ifndef NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_CONSUMER_STARTER_HPP
#define NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_CONSUMER_STARTER_HPP

#include "consumer.hpp"

#include "ns3/ndnSIM/helper/ndn-stack-helper.hpp"
#include "ns3/application.h"

namespace ns3 {

// Class inheriting from ns3::Application
class CustomConsumerStarter : public Application
{
public:
static TypeId
GetTypeId()
{
static TypeId tid = TypeId("CustomConsumerStarter")
.SetParent<Application>()
.AddConstructor<CustomConsumerStarter>();

return tid;
}

protected:
// inherited from Application base class.
virtual void
StartApplication()
{
// Create an instance of the app, and passing the dummy version of KeyChain (no real signing)
m_instance.reset(new app::CustomConsumer(ndn::StackHelper::getKeyChain()));
m_instance->run(); // can be omitted
}

virtual void
StopApplication()
{
// Stop and destroy the instance of the app
m_instance.reset();
}

private:
std::unique_ptr<app::CustomConsumer> m_instance;
};

} // namespace ns3

#endif // NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_REAL_APP_STARTER_HPP

同様にしてTrival Producerも移植してみます。

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
70
71
72
73
74
75
76
77
#ifndef NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_PRODUCER_HPP
#define NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_PRODUCER_HPP

#include <ndn-cxx/face.hpp>
#include <ndn-cxx/interest.hpp>
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/util/scheduler.hpp>

#include <iostream>

namespace app {

class CustomProducer
{
public:
CustomProducer(ndn::KeyChain& keyChain)
: m_keyChain(keyChain)
// , m_faceProducer(m_face.getIoService())
, m_scheduler(m_face.getIoService())
{
}

void
run()
{
m_face.setInterestFilter("/example/testApp",
std::bind(&CustomProducer::onInterest, this, _1, _2),
ndn::RegisterPrefixSuccessCallback(),
std::bind(&CustomProducer::onRegisterFailed, this, _1, _2));

m_face.processEvents(); // ok (will not block and do nothing)
// m_faceConsumer.getIoService().run(); // will crash
}

private:
void
onInterest(const ndn::InterestFilter& filter, const ndn::Interest& interest)
{
std::cout << "<< I: " << interest << std::endl;

ndn::Name dataName(interest.getName());
dataName
.append("testApp")
.appendVersion();

static const std::string content = "hogehogeclub";

std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>();
data->setName(dataName);
data->setFreshnessPeriod(ndn::time::seconds(10));
data->setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.size());

m_keyChain.sign(*data);

std::cout << ">> D: " << *data << std::endl;
m_face.put(*data);
}

void
onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
{
std::cerr << "ERROR: Failed to register prefix \""
<< prefix <<"\" in local hub's daemon (" << reason << ")"
<< std::endl;

m_face.shutdown();
}

private:
ndn::KeyChain& m_keyChain;
ndn::Face m_face;
ndn::Scheduler m_scheduler;
};

} // namespace app

#endif // NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_REAL_APP_HPP
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
#ifndef NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_PRODUCER_STARTER_HPP
#define NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_CUSTOM_PRODUCER_STARTER_HPP

#include "producer.hpp"

#include "ns3/ndnSIM/helper/ndn-stack-helper.hpp"
#include "ns3/application.h"

namespace ns3 {

// Class inheriting from ns3::Application
class CustomProducerStarter : public Application
{
public:
static TypeId
GetTypeId()
{
static TypeId tid = TypeId("CustomProducerStarter")
.SetParent<Application>()
.AddConstructor<CustomProducerStarter>();

return tid;
}

protected:
// inherited from Application base class.
virtual void
StartApplication()
{
// Create an instance of the app, and passing the dummy version of KeyChain (no real signing)
m_instance.reset(new app::CustomProducer(ndn::StackHelper::getKeyChain()));
m_instance->run(); // can be omitted
}

virtual void
StopApplication()
{
// Stop and destroy the instance of the app
m_instance.reset();
}

private:
std::unique_ptr<app::CustomProducer> m_instance;
};

} // namespace ns3

#endif // NDNSIM_EXAMPLES_NDN_CXX_SIMPLE_REAL_APP_STARTER_HPP

最後にscratch/下にシナリオコードを書いてみる。

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
#include "ns3/sampleapps/consumer.hpp"
#include "ns3/sampleapps/consumer-starter.hpp"

#include "ns3/sampleapps/producer.hpp"
#include "ns3/sampleapps/producer-starter.hpp"

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/ndnSIM-module.h"
#include "ns3/point-to-point-module.h"

namespace ns3 {

NS_OBJECT_ENSURE_REGISTERED(CustomConsumerStarter);
NS_OBJECT_ENSURE_REGISTERED(CustomProducerStarter);

int
main(int argc, char* argv[])
{
CommandLine cmd;
cmd.Parse(argc, argv);

// Ptr<Node> node = CreateObject<Node>();
NodeContainer nodes;
nodes.Create(2);

PointToPointHelper p2p;
p2p.Install(nodes.Get(0), nodes.Get(1));

ndn::StackHelper ndnHelper;
ndnHelper.SetDefaultRoutes(true);
ndnHelper.InstallAll();

ndn::AppHelper consumerHelper("CustomConsumerStarter");
consumerHelper.Install(nodes.Get(0))
.Start(Seconds(6.5));

ndn::AppHelper producerHelper("CustomProducerStarter");
producerHelper.Install(nodes.Get(1))
.Start(Seconds(6.5));

Simulator::Stop(Seconds(20.0));

Simulator::Run();
Simulator::Destroy();

return 0;
}

} // namespace ns3

int
main(int argc, char* argv[])
{
return ns3::main(argc, argv);
}

ndn-cxxのコードを元に書き直したhppファイル群は、元々ndnSIMに入っているモジュール群と同じようにbuild/ns3/下に置くとscratch/下に置いたシナリオコードで上手くincludeすることが出来ます。
scratch/下にフォルダを作成してそこにhppファイル群を入れた場合、どう足掻いてもinclude出来ません!!!

今回はbuild/ns3/sampleapps/下にhppファイル群を置いて実行します。

実行結果

1
2
3
4
5
6
7
8
9
$ ./build/scratch/ndn-cxx-simple
Sending: /example/testApp/randomData?ndn.MustBeFresh=1&ndn.InterestLifetime=1000
<< I: /example/testApp/randomData?ndn.MustBeFresh=1&ndn.InterestLifetime=1000&ndn.Nonce=2956930911
>> D: Name: /example/testApp/randomData/testApp/%FD%19q
MetaInfo: ContentType: 0, FreshnessPeriod: 10000 milliseconds
Content: (size: 12)
Signature: (type: SignatureSha256WithRsa, value_length: 260)

Hello!

無事に実行できました。

終わりに

ndnSIMのhelperでは出来ることが非常に限られているので、実験の際にはndn-cxxのコードみたいに書いたhppファイル群を作成して、シナリオコードからincludeすると柔軟な条件で実験が出来たりします。
この記事がNDNを研究している日本人研究者の助けになれば幸いです。
質問があればコメント欄にどうぞ。

※ 今回書いたコードはこちらからも見れます https://github.com/wawawanet/ndn-cxx_to_ndnSIM

追記

ConsumerとProducerの間にノードを増やしても、ルーティング処理をndnSIMに任せておけばパケットが到達することを確認出来たので追記しておきます。
PointToPointHelperをインストールしてノードを結んでおいて、 ndn::StackHelperをSetDefaultRoutes(true)として全ノードにインストールしておけば、パケットは自動でルーティングされて届くみたいです。
ノードがちゃんと結ばれていないとNackが返ってくるようになっています。
(ルーティングの処理がブラックボックスすぎて、ルータに機能を追加したい場合はどうやるのか分からないですね……)


Macのgcc, g++をHomebrewを使って最新版にする

モチベーション

私はデスクトップはUbuntu、ノートPCはMacを利用しているのですが、
全く同じC++のコードを両者で同じようにコンパイルした際に、MacでだけWarningが出たりします。
Command Line Toolsで入れたMacのコンパイラがよろしくないのが原因っぽいので、最新版に更新してしまいましょう。

やること

1
$ brew install gcc

インストールはこれだけで終わりです。
Homebrew経由で入れたパッケージは/usr/local/Celler/に保存され、/usr/local/bin/にコマンドのエイリアスが自動で貼られます。
/usr/local/bin/からエイリアスを探し当てます

1
2
$ ls /usr/local/bin | grep gcc
$ ls /usr/local/bin | grep g++

上記コマンドで見つかったgcc-8g++-8がお目当てのエイリアスです。
見つかったエイリアスに対してシンボリックリンクを貼ってあげましょう。

1
2
$ ln -s /usr/local/bin/gcc-8 /usr/local/bin/gcc
$ ln -s /usr/local/bin/g++-8 /usr/local/bin/g++

最後に~/.bash_profileの良い感じの場所に以下の記述をして、優先順位高めのパスを通したら終了です。

1
export PATH=$PATH:/usr/local/bin

結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/8.2.0/libexec/gcc/x86_64-apple-darwin16.7.0/8.2.0/lto-wrapper
Target: x86_64-apple-darwin16.7.0
Configured with: ../configure --build=x86_64-apple-darwin16.7.0 --prefix=/usr/local/Cellar/gcc/8.2.0 --libdir=/usr/local/Cellar/gcc/8.2.0/lib/gcc/8 --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-8 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --enable-checking=release --with-pkgversion='Homebrew GCC 8.2.0' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-nls
Thread model: posix
gcc version 8.2.0 (Homebrew GCC 8.2.0)
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/8.2.0/libexec/gcc/x86_64-apple-darwin16.7.0/8.2.0/lto-wrapper
Target: x86_64-apple-darwin16.7.0
Configured with: ../configure --build=x86_64-apple-darwin16.7.0 --prefix=/usr/local/Cellar/gcc/8.2.0 --libdir=/usr/local/Cellar/gcc/8.2.0/lib/gcc/8 --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-8 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --enable-checking=release --with-pkgversion='Homebrew GCC 8.2.0' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-nls
Thread model: posix
gcc version 8.2.0 (Homebrew GCC 8.2.0)

ns-3構築を爆速で終わらせるためのシェルスクリプト

ns-3.28をUbuntuに一気に入れてしまいたいのでシェルスクリプトを書いてみた。
動作環境 Ubuntu 16.04 or Ubuntu 17.10

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
#!/bin/sh

sudo apt install gcc g++
sudo apt install python python-dev
sudo apt install mercurial python-setuptools git
sudo apt install qt4-dev-tools libqt4-dev
sudo apt install cmake libc6-dev libc6-dev-i386 g++-multilib
sudo apt install gdb valgrind
sudo apt install gsl-bin libgsl2 libgsl-dev
sudo apt install flex bison libfl-dev
sudo apt install tcpdump
sudo apt install sqlite sqlite3 libsqlite3-dev
sudo apt install libxml2 libxml2-dev
sudo apt install libgtk2.0-0 libgtk2.0-dev
sudo apt install vtun lxc
sudo apt install uncrustify
sudo apt install doxygen graphviz imagemagick
sudo apt install texlive texlive-extra-utils texlive-latex-extra texlive-font-utils texlive-lang-portuguese dvipng
sudo apt install python-sphinx dia
sudo apt install python-pygraphviz python-kiwi python-pygoocanvas libgoocanvas-dev ipython
sudo apt install libboost-signals-dev libboost-filesystem-dev
sudo apt install openmpi-bin openmpi-common openmpi-doc libopenmpi-dev
cd ~
wget http://www.nsnam.org/release/ns-allinone-3.28.tar.bz2
tar xjf ns-allinone-3.28.tar.bz2
cd ns-allinone-3.28/ns-3.28
./waf distclean
./waf configure --enable-sudo -d optimized
#enable-sudoはビルドしたものに管理者権限を与えるようにするオプション
#-d optimizedはそのマシン用に最適化するオプション
#サンプルコードもビルドしたければ--enable-exmamples
#テストコードもビルドしたければ--enable-tests
./waf

適宜編集して使ってください。
ちなみにUbuntu18.04ではpython-pygoocanvasを入れることが出来ないので、–vizオプションでPyVizが使えません。トポロジを可視化出来なくても良いならUbuntu18.04でも動かせると思います。


【C++】boost::timerを使おうとしてコンパイルで詰まった話

はじめに

諸事情により,C++でタイマー処理を書きたくなった.
環境はMacOSであるため,WINAPIなるものを使うことは出来ません.
さて,どうしましょう?
ということでBoostライブラリを利用したが,コンパイルで詰まったので個人的にメモしておく.
(情報・表現の正確性は保証しません)

Let’s Boost!

「Boost (ブースト)とは、C++の先駆的な開発者のコミュニティ、およびそのコミュニティによって公開されているオープンソースライブラリのことを指す。」 (Wikipediaより引用)
研究でBoostライブラリに依存した某シミュレータを使っている関係上,真っ先にBoostライブラリが思い付いた.
調べたらやっぱりタイマー処理が良い感じに出来るみたいなので使っていきましょう.

Boostのインストール

1
brew install boost

インストールしたらPATHを通さなきゃいけないみたい.
.bash_profileに以下の内容を追記する.

1
2
export PATH=$PATH:/usr/local/Cellar/boost/1.65.1_1/include
export PATH=$PATH:/usr/local/Cellar/boost/1.65.1_1/lib

Homebrew使って入れたなら/usr/local/Cellar/boost/あたりに入っていると思うので適宜書き換えてください.
見つからなかったらsudo find / -name '*boost*'とでも打って検索すれば良いんじゃないですかね?

実行してみる

とりあえず動作確認してみる.
コードは https://boostjp.github.io/tips/timer.html から引用しました

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <cmath>
#include <boost/timer/timer.hpp>

int main()
{
boost::timer::cpu_timer timer; // 時間計測を開始

for (long i = 0; i < 100000000; ++i) {
std::sqrt(123.456L); // 時間のかかる処理
}

std::string result = timer.format(); // 結果文字列を取得する
std::cout << result << std::endl;
}

実行結果は以下の通り.

1
2
3
4
5
6
7
8
$ g++ timer.cpp
Undefined symbols for architecture x86_64:
"boost::timer::auto_cpu_timer::auto_cpu_timer(short)", referenced from:
_main in timer-4497dd.o
"boost::timer::auto_cpu_timer::~auto_cpu_timer()", referenced from:
_main in timer-4497dd.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation

コンパイルエラーです.困った. (wscriptが予め入っていて./wafでよしなにやってくれる某シミュレータとは勝手が違いますね……)
ということで調べてみると,私と同じところで詰まった人がいたみたい.
https://stackoverflow.com/questions/10565117/undefined-reference-to-boosttimerauto-cpu-timer

要するに-lboost_timerって書いてboost_timerへリンクしてあげないといけないみたいです.
ということで以下のようにして実行

1
2
3
$ g++ timer.cpp -lboost_timer
$ ./a.out
0.323706s wall, 0.320000s user + 0.000000s system = 0.320000s CPU (98.9%)

ちゃんと動いた.めでたしめでたし.

Boostで他の機能を使う場合に-lboost_systemとか書く場合もあるらしい.
あとBoost.Pythonを使うときには-lboost_pythonとか書く.
とりあえずPATH通してても動かなかったら -lboost_<name>みたいなコンパイルオプション調べれば良さそう?


Hexoを使ってブログ作成, Github Pagesにデプロイするまで

モチベーション

・自分のWebページが欲しいけど、レンタルサーバ代は払いたくない
・動的なページである必要性はない
→ Github Pagesでいい感じのものを作ってしまえばいいじゃん!
という感じ

どうやって作るか

https://qiita.com/okmttdhr/items/82ecb0332835472e905f
にあるように、Github Pagesでブログ構築ができる静的サイトジェネレーターはたくさんあるっぽい。
Railsベースのものはなんとなく使いたくないので、ノリでHexoを選んだ。

制作環境

MacOS Sierra 10.12.6
Homebrewはインストール済

作っていく

Github Pages用のRepository作成

Githubにアクセス → 右上の「+」ボタンから「New Repository」を選択
Repository Nameは username.github.io にすること!
URLがめんどくさくなります。
※この記事内でusernameと書かれているところは適宜Githubのユーザ名に置き換えて実行してください。

Hexoのインストール

nodebrewとかいうバージョン管理ツール?(RubyのrbenvとかPythonのpyenvみたいな立ち位置のもの)があるらしいのでそれを使っていく
Homebrewを使ってnodebrewをインストール

1
$ brew install nodebrew

今時の若者のShellはfishなので、~/.config/fish/conf.d/config.fishに以下の通り追記する。

1
set -x PATH $HOME/.nodebrew/current/bin $PATH

Shellを起動し直してから、最新のNode.jsをインストール

1
2
3
4
5
6
7
8
9
10
11
12
13
$ nodebrew setup
$ nodebrew install-binary latest
$ nodebrew list
v11.9.0

current: none
$ nodebrew use v11.9.0
$ nodebrew list
v11.9.0

current: v11.9.0
$ npm -v
6.5.0

npmを更新しておく

1
2
3
$ npm update -g npm
$ npm -v
6.7.0

Node.jsが入ったのでHexoとデプロイツールを入れていく

1
2
$ npm install -g hexo
$ sudo npm install hexo-deployer-git --save

とりあえず立ち上げてみる

適当なディレクトリを作ってそこに環境を構築する

1
2
3
4
$ mkdir blog
$ cd blog
$ hexo init
$ hexo server

localhost:4000にアクセス出来たらとりあえず成功です。

configの修正

以降ではhexo initを実行したディレクトリ下で作業する
デプロイ先などの設定が_config.ymlにあるので修正する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Site
title: hoge
subtitle: hogefuga
description:
keywords:
author: piyo
language: ja
timezone: Asia/Tokyo

# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: https://username.github.io/
root: /
permalink: :year/:month/:day/:title/
permalink_defaults:

# Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
type: git
repo: git@github.com:username/username.github.io.git
branch: master

この辺を修正してあげれば良い

デプロイしてみる

1
2
3
$ git init
$ git remote add origin git@github.com:username/username.github.io.git
$ hexo deploy -g

しばらく待って、https://username.github.io にアクセス出来たら成功です!
お疲れ様でした。

新しい記事を作る場合

hexo new <記事名>と打てばsource/_posts/<記事名>.mdというものが生成されるので、生成された<記事名>.mdにMarkdown形式で入力すれば良い。
記事が完成したらhexo deploy -gと打てば良い感じにデプロイしてくれます。