モチベーション 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を利用して書いたhoge.cpp
というファイルをコンパイルして実行する場合、hoge.cpp
をexamples/
下に置いて1 2 3 $ ./waf configure --with-examples #以前configureしてある方は省略 $ ./waf $ ./build/exsamples/hoge
として実行していたものが、更新したら実行出来なくなっていて、 色々探した結果
とやって実行するように仕様が変更されていた…… せめてREADMEには書いておいてくれよ…..
モチベーション 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が返ってくるようになっています。 (ルーティングの処理がブラックボックスすぎて、ルータに機能を追加したい場合はどうやるのか分からないですね……)
モチベーション 私はデスクトップはUbuntu、ノートPCはMacを利用しているのですが、 全く同じC++のコードを両者で同じようにコンパイルした際に、MacでだけWarningが出たりします。 Command Line Toolsで入れたMacのコンパイラがよろしくないのが原因っぽいので、最新版に更新してしまいましょう。
やること
インストールはこれだけで終わりです。 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-8
とg++-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.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++でタイマー処理を書きたくなった. 環境はMacOSであるため,WINAPIなるものを使うことは出来ません. さて,どうしましょう? ということでBoostライブラリを利用したが,コンパイルで詰まったので個人的にメモしておく. (情報・表現の正確性は保証しません)
Let’s Boost! 「Boost (ブースト)とは、C++の先駆的な開発者のコミュニティ、およびそのコミュニティによって公開されているオープンソースライブラリのことを指す。」 (Wikipediaより引用) 研究でBoostライブラリに依存した某シミュレータを使っている関係上,真っ先にBoostライブラリが思い付いた. 調べたらやっぱりタイマー処理が良い感じに出来るみたいなので使っていきましょう.
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>
みたいなコンパイルオプション調べれば良さそう?
モチベーション ・自分の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をインストール
今時の若者の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
と打てば良い感じにデプロイしてくれます。