Last active
September 24, 2015 00:37
-
-
Save gintenlabo/640298 to your computer and use it in GitHub Desktop.
pImpl イディオム + thin template 技法の簡単な例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Stack.hpp" | |
#include <vector> | |
// Impl_ の実装(今回はメンバのみ) | |
// 複雑なクラスの場合は Impl_ 内にメンバ関数を定義するのもアリ | |
struct StackImpl_::Impl_ | |
{ | |
std::vector< boost::shared_ptr<void> > v; | |
}; | |
// StackImpl_ のメンバ関数の実装 | |
StackImpl_::StackImpl_() | |
: pImpl_( new Impl_() ) {} | |
StackImpl_::~StackImpl_() /*noexcept*/ {} | |
void StackImpl_::swap( StackImpl_& other ) /*noexcept*/ { | |
pImpl_->v.swap( other.pImpl_->v ); | |
} | |
void StackImpl_::push( boost::shared_ptr<void> const& p ) { | |
pImpl_->v.push_back( p ); | |
} | |
void* StackImpl_::top() const /*noexcept*/ { | |
return pImpl_->v.back().get(); | |
} | |
void StackImpl_::pop() { | |
pImpl_->v.pop_back(); | |
} | |
std::size_t StackImpl_::size() const /*noexcept*/ { | |
return pImpl_->v.size(); | |
} | |
bool StackImpl_::empty() const /*noexcept*/ { | |
return pImpl_->v.empty(); | |
} | |
void* StackImpl_::operator[]( std::size_t i ) const /*noexcept*/ { | |
return pImpl_->v[i].get(); | |
} | |
void StackImpl_::reserve( std::size_t n ) { | |
return pImpl_->v.reserve(n); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef INCLUDED_STACK_HPP_ | |
#define INCLUDED_STACK_HPP_ | |
// pImpl なテンプレートクラス | |
#include <cstddef> | |
#include <boost/shared_ptr.hpp> | |
#include <boost/make_shared.hpp> | |
#include <boost/scoped_ptr.hpp> | |
// #include <utility> // move を使うなら | |
// 実装用クラス(これを pImpl にする) | |
// 実際には Stack<T> からしかアクセスできないよう、 | |
// 全て private にして Stack<T> を friend 宣言したほうがいい | |
struct StackImpl_ | |
{ | |
StackImpl_(); | |
~StackImpl_() /*noexcept*/; | |
void swap( StackImpl_& other ) /*noexcept*/; | |
// move | |
// StackImpl_( StackImpl_&& src ) /*noexcept*/; | |
void push( boost::shared_ptr<void> const& ); | |
void* top() const /*noexcept*/; | |
void pop(); | |
std::size_t size() const /*noexcept*/; | |
bool empty() const /*noexcept*/; | |
void* operator[]( std::size_t i ) const /*noexcept*/; | |
void reserve( std::size_t n ); | |
private: | |
class Impl_; | |
boost::scoped_ptr<Impl_> pImpl_; | |
}; | |
// 実際に使うのは実装用クラスの薄いラッパ(thin wrapper) | |
template<class T> | |
struct Stack | |
{ | |
Stack() {} | |
// ただし copy は(値の semantic を採用した場合)薄いラッパでは無理 | |
Stack( Stack const& src ) | |
{ | |
// 深いコピーをする | |
std::size_t const n = src.size(); | |
StackImpl_ const& src_ = src.impl_; | |
impl_.reserve( n ); | |
for( std::size_t i = 0; i < n; ++i ) { | |
this->push( *static_cast<T const*>( src_[i] ) ); | |
} | |
} | |
// move | |
// Stack( Stack && src ) /*noexcept*/ | |
// : impl_( std::move( src.impl_ ) ) {} | |
// swap | |
void swap( Stack& other ) /*noexcept*/ { | |
impl_.swap( other.impl_ ); | |
} | |
friend void swap( Stack& one, Stack& another ) /*noexcept*/ { | |
one.swap( another ); | |
} | |
// operator= | |
Stack& operator=( Stack const& rhs ) { | |
Stack( rhs ).swap( *this ); | |
return *this; | |
} | |
// Stack& operator=( Stack&& rhs ) /*noexcept*/ { | |
// this->swap( rhs ); | |
// return *this; | |
// } | |
// 要素を push する | |
void push( T const& x ){ | |
impl_.push( boost::make_shared<T>(x) ); | |
} | |
// スタックトップの要素を得る | |
T& top() /*noexcept*/ { | |
return *static_cast<T*>( impl_.top() ); | |
} | |
T const& top() const /*noexcept*/ { | |
return *static_cast<T const*>( impl_.top() ); | |
} | |
// 要素を pop する | |
void pop() { | |
impl_.pop(); | |
} | |
// 大きさを得る | |
std::size_t size() const /*noexcept*/ { | |
return impl_.size(); | |
} | |
// 空かどうかチェックする | |
bool empty() const /*noexcept*/ { | |
return impl_.empty(); | |
} | |
private: | |
StackImpl_ impl_; | |
}; | |
#endif // #ifndef INCLUDED_STACK_HPP_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Stack.hpp" | |
// とりあえず全メンバを明示的に実体化させておく | |
// 組み込み型 | |
template class Stack<int>; | |
#include <string> | |
#include <cassert> | |
// 組み込みじゃない型 | |
struct hoge | |
{ | |
// 構築とか | |
hoge( std::string const& x_ = std::string() ) | |
: x( x_ ) { ++instances_(); } | |
hoge( hoge const& src ) | |
: x( src.x ) { ++instances_(); } | |
~hoge(){ --instances_(); } | |
// 現存するインスタンスの総数を返す | |
static int instances() { | |
int const result = instances_(); | |
assert( result >= 0 ); | |
return instances_(); | |
} | |
// 適当にメンバを、せっかくなので非PODで | |
std::string x; | |
private: | |
// インスタンスの総数 | |
static int& instances_() { | |
static int counter = 0; | |
return counter; | |
} | |
}; | |
template class Stack<hoge>; | |
#include <boost/test/minimal.hpp> | |
int test_main( int, char** ) | |
{ | |
Stack<int> stack; | |
BOOST_CHECK( stack.empty() ); | |
BOOST_CHECK( stack.size() == 0 ); | |
stack.push(1); | |
stack.push(2); | |
stack.push(3); | |
stack.push(4); | |
stack.push(5); | |
BOOST_CHECK( stack.size() == 5 ); | |
BOOST_CHECK( stack.top() == 5 ); | |
// コピーテスト | |
{ | |
// 空の Stack のコピー | |
{ | |
Stack<int> orig; | |
Stack<int> copied = orig; | |
BOOST_CHECK( orig.size() == 0 && copied.size() == 0 ); | |
// コピー元と無関係なことを確認 | |
copied.push(0); | |
BOOST_CHECK( orig.size() == 0 && copied.size() == 1 && copied.top() == 0 ); | |
orig.push(23); | |
BOOST_CHECK( orig.size() == 1 && orig.top() == 23 && | |
copied.size() == 1 && copied.top() == 0 ); | |
copied.pop(); | |
BOOST_CHECK( orig.size() == 1 && orig.top() == 23 && copied.size() == 0 ); | |
} | |
// 値のある Stack のコピー | |
Stack<int> const& orig = stack; | |
std::size_t const n = orig.size(); | |
int const top = orig.top(); | |
// copy test | |
Stack<int> copied = orig; | |
BOOST_CHECK( copied.size() == n && copied.top() == top ); | |
// コピーしたスタックの先頭を書き換えても | |
// オリジナルに影響しないことを確認 | |
copied.top() = 42; | |
BOOST_CHECK( orig.top() == top ); | |
// pop してもおk? | |
copied.pop(); | |
BOOST_CHECK( orig.size() == n ); | |
// 順々に取り除いていく | |
BOOST_CHECK( copied.size() == 4 ); | |
BOOST_CHECK( copied.top() == 4 ); | |
copied.pop(); | |
BOOST_CHECK( copied.size() == 3 ); | |
BOOST_CHECK( copied.top() == 3 ); | |
copied.pop(); | |
BOOST_CHECK( copied.size() == 2 ); | |
BOOST_CHECK( copied.top() == 2 ); | |
copied.pop(); | |
BOOST_CHECK( copied.size() == 1 ); | |
BOOST_CHECK( copied.top() == 1 ); | |
copied.pop(); | |
// 空っぽ。 | |
BOOST_CHECK( copied.empty() ); | |
// オリジナルは変化なし | |
BOOST_CHECK( orig.size() == n ); | |
BOOST_CHECK( orig.top() == top ); | |
} | |
// 代入 | |
{ | |
Stack<int> temp; | |
temp = stack; | |
BOOST_CHECK( temp.size() == stack.size() && temp.top() == stack.top() ); | |
// clear | |
temp = Stack<int>(); // あるいは Stack<int>().swap( temp ); | |
BOOST_CHECK( temp.empty() ); | |
// swap | |
temp.swap( stack ); | |
BOOST_CHECK( !temp.empty() && stack.empty() ); | |
swap( temp, stack ); | |
BOOST_CHECK( temp.empty() && !stack.empty() ); | |
} | |
// 操作(コピー先をいじってるけど、もう一度) | |
stack.push(6); | |
BOOST_CHECK( stack.size() == 6 ); | |
BOOST_CHECK( stack.top() == 6 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 5 ); | |
BOOST_CHECK( stack.top() == 5 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 4 ); | |
BOOST_CHECK( stack.top() == 4 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 3 ); | |
BOOST_CHECK( stack.top() == 3 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 2 ); | |
BOOST_CHECK( stack.top() == 2 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 1 ); | |
BOOST_CHECK( stack.top() == 1 ); | |
stack.pop(); | |
BOOST_CHECK( stack.size() == 0 ); | |
BOOST_CHECK( stack.empty() ); | |
// 大量に push | |
std::size_t const n = 100000; | |
for( std::size_t i = 0; i < n; ++i ) { | |
stack.push(i); | |
} | |
BOOST_CHECK( stack.size() == n ); | |
for( int i = n - 1; i >= 0; --i ) { | |
BOOST_CHECK( stack.top() == i ); | |
stack.pop(); | |
} | |
BOOST_CHECK( stack.empty() ); | |
// hoge についても適当に。 | |
{ | |
// 今までのインスタンスの総数 | |
int const init_instances = hoge::instances(); | |
// 一応ゼロであるはずなのでチェックしておく(なくてもいい) | |
BOOST_CHECK( init_instances == 0 ); | |
// 空のスタックを構築 | |
Stack<hoge> stack; | |
// インスタンス数は変化しない | |
BOOST_CHECK( hoge::instances() == init_instances ); | |
// push | |
stack.push( hoge("foo") ); | |
// ひとつ増える | |
BOOST_CHECK( hoge::instances() == init_instances + 1 ); | |
BOOST_CHECK( stack.top().x == "foo" ); | |
// コピー | |
{ | |
Stack<hoge> temp = stack; | |
// もひとつ増える | |
BOOST_CHECK( hoge::instances() == init_instances + 2 ); | |
BOOST_CHECK( temp.top().x == "foo" ); | |
} | |
// 破棄されてインスタンス数は減少する | |
BOOST_CHECK( hoge::instances() == init_instances + 1 ); | |
// さらにpop | |
stack.pop(); | |
BOOST_CHECK( hoge::instances() == init_instances ); | |
// 空になりました | |
BOOST_CHECK( stack.empty() ); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment