std::mapで参照
題名通りなんですがstd::mapで参照を使いたい時は
std::map<Key, Value&> map;
がいいのか、それとも
std::map<Key, std::reference_wrapper<Value>> map;
のどちらがいいのか。
というのも過去にどこかで「参照のmapはダメだ」というのを聞いたような気がしたので。
一応どちらも使ってみたのですが
#include <iostream> #include <map> #include <functional> template<typename T1, typename T2> static std::pair<T1, T2&> make_ref_pair(T1 key, T2& elem) { return std::pair<T1, T2&>(key, elem); } template<typename T1, typename T2> void print_map(std::map<T1, T2> const& map) { for (auto& pair : map) std::cout << "pair: first=" << pair.first << " second=" << pair.second << std::endl; } int main() { std::cout << "std::map<int, int&> version" << std::endl; int num = 5; std::cout << "num = " << num << std::endl; std::map<int, int&> mapper; mapper.insert(make_ref_pair(1, num)); print_map(mapper); ++num; print_map(mapper); ++mapper.at(1); print_map(mapper); return 0; }
#include <iostream> #include <map> #include <functional> template<typename T1, typename T2> void print_map(std::map<T1, T2> const& map) { for (auto& pair : map) std::cout << "pair: first=" << pair.first << " second=" << pair.second << std::endl; } int main() { std::cout << "std::map<int, std::reference_wrapper<int>> version" << std::endl; int num = 5; std::cout << "num = " << num << std::endl; std::map<int, std::reference_wrapper<int>> mapper; mapper.insert(std::make_pair(1, std::ref(num))); print_map(mapper); ++num; print_map(mapper); ++mapper.at(1); print_map(mapper); return 0; }
どちらも出力が
num = 5 pair: first=1 second=5 pair: first=1 second=6 pair: first=1 second=7
と同じ。
まぁ、いいか。
BumblbeeなArch Linuxでpaeカーネル用の現時点での最新nvidiaドライバー(310.19)を使う
自分はDELL XPS15をメイン機に使っています。
こいつはNVIDIA GT540Mを積んでいるのですが、面倒なことにGPU切り替え技術「Optimus Technology」が採用されています。
普通にはNVIDIAの公式ドライバーは使えず、Bumblebeeというオープンソースプロジェクトを使用してやっとNVIDIAのボードが使えるという状況です。
最近、Arch Linuxのカーネルをcore/linuxからlinux-paeに変えたのでBumblebeeとnvidiaドライバーが動かなくなり、AURにPAE用のnvidia-bumblebeeであるnvidia-bumblebee-paeを見つけたのですが、バージョンが304.32と古い。
なのでPKGBUILDを少し変更して最新版の310.19を使えるようにしてみました。
pkgname=nvidia-bumblebee-pae pkgver=310.19 _extramodules="extramodules-`uname -r | cut -c1-3`-pae" pkgrel=1 pkgdesc="NVIDIA drivers for linux. Packaged for Bumblebee. PAE version" arch=('i686') url="http://www.nvidia.com/" depends=('linux-pae' "nvidia-utils-bumblebee=${pkgver}") provides=("nvidia=${pkgver}") makedepends=('linux-pae-headers') conflicts=('nvidia' 'nvidia-96xx' 'nvidia-173xx' 'dkms-nvidia') license=('custom') install=nvidia.install options=(!strip) _arch='x86' _pkg="NVIDIA-Linux-${_arch}-${pkgver}" source=("ftp://download.nvidia.com/XFree86/Linux-${_arch}/${pkgver}/${_pkg}.run") md5sums=('2adbdd38540b3a8955714760e05f575d') build() { _kernver="$(cat /usr/lib/modules/${_extramodules}/version || true)" cd "${srcdir}" sh "${_pkg}.run" --extract-only cd "${_pkg}/kernel" make SYSSRC=/usr/lib/modules/"${_kernver}/build" module } package() { install -D -m644 "${srcdir}/${_pkg}/kernel/nvidia.ko" \ "${pkgdir}/usr/lib/modules/${_extramodules}/nvidia.ko" install -d -m755 "${pkgdir}/usr/lib/modprobe.d" echo "blacklist nouveau" >> "${pkgdir}/usr/lib/modprobe.d/nouveau_blacklist.conf" sed -i -e "s/EXTRAMODULES='.*'/EXTRAMODULES='${_extramodules}'/" "${startdir}/nvidia.install" gzip "${pkgdir}/usr/lib/modules/${_extramodules}/nvidia.ko" }
バージョンとmd5sum変えただけです、はい。
nvidia.installはAURにあるnvidia-bumblebee-paeをそのまま使えるはずです。
一応、自分の環境では動いてるようです。
How to extend Boost.Asio
この記事はC++ Advent Calendar 2012 12日目の記事です。
Boost.Asio、みなさんもお使いのことと存じますが、あれこのソケットなかったっけ? とかこれもAsioで使いたいなぁと思ったことはありませんか?
そんな時のためにこの記事ではBoost.AsioのProtocol, socket option, serviceの拡張を扱います。
基礎知識:
I/O service: ご存知boost::asio::io_service
I/O object: 実際にAsioを使う人が触るobject。socketやtimerなど。(例: boost::asio::tcp::socket, boost::asio::deadline_timer等)
Service: boost::asio::io_service::serviceを継承しI/O serviceに登録される。I/O objectはこのServiceをテンプレート引数に取りI/O objectに対する命令(send, recv, wait等)はServiceに渡される。(例: boost::asio::stream_sockets_service, boost::asio::deadline_timer_service等)
もしこれから説明するBoost.Asioのendpointやprotocolのイメージがつかみにくい方は
The BSD Socket API and Boost.Asio
www.boost.org/doc/html/boost_asio/overview/networking/bsd_sockets.html
をよんでいただくとソケットプログラミングの経験がある方にはaddress_v4がin_addrにendpointがsocketaddrに対応することなどがよくわかるかと思います。
・Protocolの拡張
まずはProtocolの拡張をしてみます。
ここでは例としてbasic_raw_socketにプロトコルを追加してみます。
Asioにはraw socketに対するサポートが有りますが、対応しているのはicmpだけです。
そこでtcpを扱うraw socketのプロトコルを追加してみます。
ようは
int raw_tcp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
をBoost.Asioで使える用にしてみます。
template<typename Protocol, typename Service> class basic_socket;
を継承したbasic_stream_socket, basic_datagram_socket, basic_raw_socketのそれぞれの第二テンプレート引数は
stream_sockets_service, datagram_socket_service, raw_socket_serviceがデフォルト引数になっています。
なので第一引数のProtocolだけ指定すればいいです。
Protocolはシステムコールレベルではsocket(2)の3つの引数に相当します。
socket(2)の定義は
int socket(int domain, int type, int protocol);
です。
Asioのソケットが扱えるプロトコルは
Protocol requirements:
http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/reference/Protocol.html
を満たさなければなりません。
上記のページにはXをプロトコル、aをXのオブジェクトとした時
domainはa.family()、typeはa.type()、protocolにはa.protocol()が対応すると書いて有ります。
つまりa.family()はAF_INET、a.type()はSOCK_RAW、a.protocol()はIPPROTO_TCPを返せばいい訳です。
また、X::endpointを定義しなければなりませんがこれはAsioのboost::asio::ip::basic_endpointが使えます。
そこでこんなクラスを用意しました。
template<int Family, int FamilyV6, int Type, int Protocol> class basic_raw_protocol { public: typedef basic_endpoint<basic_raw_protocol> endpoint; typedef basic_raw_socket<basic_raw_protocol> socket; typedef basic_resolver<basic_raw_protocol> resolver; static basic_raw_protocol v4() { return basic_raw_protocol(Protocol, Family); } static basic_raw_protocol v6() { return basic_raw_protocol(Protocol, FamilyV6); } int family() const { return family_; } int type() const { return Type; } int protocol() const { return protocol_; } friend bool operator==(const basic_raw_protocol& p1, const basic_raw_protocol& p2) { return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_; } friend bool operator!=(const basic_raw_protocol& p1, const basic_raw_protocol& p2) { return p1.protocol_ != p2.protocol_ || p1.family_ != p2.family_; } private: explicit basic_raw_protocol(int protocol_id, int protocol_family) : protocol_(protocol_id), family_(protocol_family) { } int protocol_; int family_; };
追記: IPv6用のファミリーを指定し忘れていたのを修正
これをtypedefして
typedef basic_raw_protocol<AF_INET, AF_INET6, SOCK_RAW, IPPROTO_TCP> raw_tcp;
などとしておきます。これでbasic_raw_socketにTCPを扱うraw socketが追加出来ました。
使い方はboost::asio::ip::icmpと全く同じです。
ちなみにlibarexという自分用の小さいライブラリを作っており、その中でLinuxのpacket socketを使用できるよう拡張した時
basic_raw_protocolの第三引数のProtocolにホストバイトオーダからネットワークバイトオーダ変換する必要がありました。
そんな時は
constexpr std::uint16_t htons(std::uint16_t s) { return (s >> 8) | (s << 8); }
みたいな関数を作っておけばネットワークバイトオーダでテンプレート引数に渡せます。
・ソケットオプション
もう基本的にType Requirementsを満たせば何でもできます。
ソケットのソケットオプションを取得するには
Gettable socket option requirements
www.boost.org/doc/html/boost_asio/reference/GettableSocketOption.html
ソケットに渡すソケットオプションは
Settable socket option requirements
http://www.boost.org/doc/html/boost_asio/reference/SettableSocketOption.html
を満たせば良いわけです。それぞれ例のごとくPOSIXの関数であるgetsockopt(2), setsockopt(2)
int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len); int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
に渡せるよう、level(), name(), data(), size()メンバー関数を定義すれば良いです。
注意点としてGettableの場合data()メンバー関数は(void *)に変換可能なポインターを
Settableではdata()メンバー関数は(void const *)に変換可能なポインターを返さねばなりません、これだけです。
試しにこんなクラスを用意しました。
template<int Level, int Name, typename ValueType = int> class basic_option { public: basic_option() = default; basic_option(ValueType const& value) : optval_(value) { } template<typename Protocol> int level(Protocol const& p) const { return Level; } template<typename Protocol> int name(Protocol const& p) const { return Name; } template<typename Protocol> void *data(Protocol const &p) { // Should return a pointer that is convertible to void* return reinterpret_cast<void *>(&optval_); } template<typename Protocol> void const *data(Protocol const& p) const { // Should return a pointer that is convertible to void* return reinterpret_cast<void const *>(&optval_); } template<typename Protocol> size_t size(Protocol const& p) const { // size() returns the size of *data() return sizeof(optval_); } template <typename Protocol> void resize(Protocol const& p, std::size_t s) { if (s != sizeof(optval_)) { std::length_error ex("basic_option resize error"); boost::throw_exception(ex); } } void set_value(ValueType const& val) { optval_ = val; } ValueType const& get_value() const { return optval_; } private: ValueType optval_; };
使い方としてSO_RCVTIMEOオプションを扱ってみます。
こんな感じで書けます。
#include <iostream> #include <boost/asio.hpp> #include <boost/assert.hpp> namespace asio = boost::asio; void print_opt(struct timeval const& tv) { std::cout << "socket option: sec=" << tv.tv_sec << " usec=" << tv.tv_usec << std::endl; } int main(int argc, char const* argv[]) { asio::io_service io; asio::ip::tcp::socket socket(io, asio::ip::tcp::v4()); typedef basic_option< SOL_SOCKET, SO_RCVTIMEO, struct timeval > recvtimeo; recvtimeo getopt; socket.get_option(getopt); print_opt(getopt.get_value()); struct timeval recvtv = {5, 0}; recvtimeo opt(recvtv); socket.set_option(opt); recvtimeo afteropt; socket.get_option(afteropt); print_opt(afteropt.get_value()); BOOST_ASSERT(opt.get_value().tv_sec == afteropt.get_value().tv_sec); BOOST_ASSERT(opt.get_value().tv_usec == afteropt.get_value().tv_usec); return 0; }
・I/O objectとServiceの拡張
ここでは超手抜きのタイマーを作ります。(もはや某所の翻訳)
必要なもの:
boost::asio::basic_io_objectを継承するI/Oオブジェクト。(easy_timer)
boost::asio::io_service::serviceを継承するService。(basic_timer_service)
Serviceの実装(implementation)クラス (timer_impl)
順番に説明していきます。
まず、下に示すeasy_timerを見て下さい。
#include <boost/asio.hpp> template <typename Service> class easy_timer : public boost::asio::basic_io_object<Service> { public: explicit easy_timer(boost::asio::io_service& io_service) : boost::asio::basic_io_object<Service>(io_service) { } void wait(std::size_t seconds) { return this->get_service().wait(this->get_implementation(), seconds); } template <typename Handler> void async_wait(std::size_t seconds, Handler handler) { this->get_service().async_wait(this->get_implementation(), seconds, handler); } };
このクラスは手抜タイマーのI/Oオブジェクトです。実際にAsioを使う人が触るobjectです。
basic_io_object継承することにより、このI/Oオブジェクトがインスタンス化された時Serviceは自動的にI/O serviceに登録されます。
テンプレート引数のServiceへはbasic_io_objectのget_service()でアクセスできます。処理は基本的にServiceに丸投げします。
その時、一緒に実装(this->get_implementation())を取得し渡します。
(get_service(), get_implementation()の代わりにservice, implementatioデータメンバーも有りますがどちらも現在ではdeprecatedです。)
要点は:
・Serviceの登録とServiceの実装の生成は親クラスのbasic_io_objectがする。
・メンバー関数(wait, async_wait等)は基本的にServiceに実装(this->get_implementation())を渡して丸投げ。
です。
次はServiceことbasic_timer_serviceです。
#include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/scoped_ptr.hpp> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <boost/system/error_code.hpp> template <typename TimerImplementation = timer_impl> class basic_timer_service : public boost::asio::io_service::service { public: static boost::asio::io_service::id id; explicit basic_timer_service(boost::asio::io_service& io_service) : boost::asio::io_service::service(io_service), async_work_(new boost::asio::io_service::work(async_io_service_)), async_thread_(boost::bind(&boost::asio::io_service::run, &async_io_service_)) { } ~basic_timer_service() { async_work_.reset(); async_io_service_.stop(); async_thread_.join(); } typedef boost::shared_ptr<TimerImplementation> implementation_type; void construct(implementation_type& impl) { impl.reset(new TimerImplementation()); } void destroy(implementation_type& impl) { impl->destroy(); impl.reset(); } void wait(implementation_type& impl, std::size_t seconds) { boost::system::error_code ec; impl->wait(seconds, ec); boost::asio::detail::throw_error(ec); } template <typename Handler> class wait_operation { public: wait_operation(implementation_type& impl, boost::asio::io_service& io_service, std::size_t seconds, Handler handler) : impl_(impl), io_service_(io_service), work_(io_service), seconds_(seconds), handler_(handler) { } void operator()() const { implementation_type impl = impl_.lock(); if (impl) { boost::system::error_code ec; impl->wait(seconds_, ec); this->io_service_.post(boost::asio::detail::bind_handler(handler_, ec)); } else { this->io_service_.post(boost::asio::detail::bind_handler(handler_, boost::asio::error::operation_aborted)); } } private: boost::weak_ptr<TimerImplementation> impl_; boost::asio::io_service &io_service_; boost::asio::io_service::work work_; std::size_t seconds_; Handler handler_; }; template <typename Handler> void async_wait(implementation_type& impl, std::size_t seconds, Handler handler) { this->async_io_service_.post(wait_operation<Handler>(impl, this->get_io_service(), seconds, handler)); } private: void shutdown_service() { } boost::asio::io_service async_io_service_; boost::scoped_ptr<boost::asio::io_service::work> async_work_; boost::thread async_thread_; }; template <typename TimerImplementation> boost::asio::io_service::id basic_timer_service<TimerImplementation>::id;
Boost.Asioに揃えるためにServiceは以下のような事を満たさねばなりません。
・boost::asio::io_service::serviceを継承し、コンストラクタがboost::asio::io_service::serviceのコンストラクタに渡されたI/O servieへの参照を受け取ること。
・いかなるServiceも一意に識別するためにpublicでstaticなboost::asio::io_service::idを持つ。
・implementation_type(TimerImplementationのshared_ptr)を受け取るconstruct()とdestruct()というメンバー関数を持つ。
・shutdown_service()メンバー関数を持つ。この関数はprivateにでき、多くの場合何もしない関数である。
async_wait()はwait()の非同期版です。
非同期処理を実現するためにこの例ではboost::threadを使っています。
boost::threadで実行するのはService用に用意されたio_serviceであるasync_io_service_のboost::asio::io_service::run()です。
run()関数はServiceのコンストラクタで実行する非同期処理がない状態で実行されるのでboost::asio::io_service::workですぐにreturnするのを防ぎます。
async_io_service_にpost()するのはoperator()をオーバーロードしたwait_operation関数オブジェクトです。
operator()()ではimplementationがdestruct()されているかもしれないのでTimerImplementationのshared_ptrであるimpl引数からweak_ptrを作ってlock()で有効であるか確認してから
同期版wait()と同じようにimplementationのwait()を呼び出しその後、handler_を呼び出します。
最後にimplementationことtimer_implです。
#include <boost/system/error_code.hpp> #include <cstddef> #include <unistd.h> class timer_impl { public: timer_impl() { } ~timer_impl() { } void destroy() { } void wait(std::size_t seconds, boost::system::error_code& ec) { int ret = sleep(seconds); if (ret == 0) ec = boost::system::error_code(); else ec = boost::asio::error::operation_aborted; } };
はい、もう解説はいらないでしょう。ただ呼び出しスレッドをsleep(3)するだけです。(超簡易なのでお許しを)
以上です。こんなふうに使えます。
#include <boost/asio.hpp> #include <iostream> #include <chrono> #include <functional> // // timer_impl, basic_timer_service, easy_timerをincludeする // // 今回作ったタイマー typedef easy_timer<basic_timer_service<> > etimer; // Asioのタイマー typedef boost::asio::basic_waitable_timer<std::chrono::steady_clock> ctimer; void wait_handler(boost::system::error_code const& ec, int sec) { std::cout << sec << " sec" << std::endl; } int main() { boost::asio::io_service io_service; etimer timer(io_service); timer.async_wait(5, std::bind(&wait_handler, std::placeholders::_1, 5)); ctimer timer2(io_service, std::chrono::seconds(3)); timer2.async_wait(std::bind(&wait_handler, std::placeholders::_1, 3)); std::cout << "[*] Main: start io_service.run()" << std::endl; io_service.run(); std::cout << "[*] Main: finished io_service.run()" << std::endl; return 0; }
3秒後に"3 sec", 5秒後に"5 sec"と表示されて終わるはずです。
以上でひと通り荒いですが、終了です。
もし、もうちょっとコード読みたいなぁって方はgithubに
GitHub - pfpacket/libarex: Asio Rawsocket EXtension
こんなもの上げてるので良かったらどうぞ。
次はid:lnseabさんです。
printk(KERN_* ...) より pr_*()を使う
Linuxでデバイスドライバを作るとき標準出力が無いので代わりにカーネルバッファに情報やエラーを出力します。
普段はprintk()を使っているのですが、思いたちでLinuxのメーリングリストに投げるパッチの体裁をチェックするscripts/checkpatch.plに書けてみると
WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ...
というWARNINGがでます。
どうやらprintk()のかわりにネットワークデバイスなどでnet_device構造体が使えるときはnetdev_info()をそれ以外ではpr_*をつかえとのようです。
printk(KERN_INFO ...); → pr_info(...);
printk(KERN_ALERT ...); → pr_alert(...);
メーリングリストにもそういったパッチがちらほら流れてますね。
# printk should use KERN_* levels. Note that follow on printk's on the # same line do not need a level, so we use the current block context # to try and find and validate the current printk. In summary the current # printk includes all preceding printk's which have no newline on the end. # we assume the first bad printk is the one to report.
scripts/checkpatch.plより
■
前の記事でイーサネットフレームを非同期に受信するプログラムについて書きましたが、
あれを起動している最中、イーサネットフレームのtypeが0x26なものが出てきました。
この値はarex::ether_typeのどの値にも該当しません。
ちょっと気になったので調べてみると、どうやら流れているイーサネットフレームには「DIX規格」と「IEEE 802.3規格」という2つの種類があるようです。
この2つの違いはDIXでのtypeフィールドが802.3規格ではtype/lengthであるということです。
wikipediaによると、この値が1501以上ならDIXだそうなので、これらを判断するためにarex::ethernet_headerにis_dix()とis_802_3()を追加しました。
参考:
Wikipedia / イーサネット
http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%BC%E3%82%B5%E3%83%8D%E3%83%83%E3%83%88