Skip to content

Instantly share code, notes, and snippets.

@gintenlabo
Created November 28, 2010 21:45
Show Gist options
  • Save gintenlabo/719324 to your computer and use it in GitHub Desktop.
Save gintenlabo/719324 to your computer and use it in GitHub Desktop.
Boost.InPlaceFactory の C++0x 移植
//
// in_place_factory:
// Boost.InPlaceFactory の C++0x 拡張
//
// boost::in_place を variadic templates + perfect forward に対応させた版です。
// 普通の apply に加え、rvalue を move させる move_apply が追加されています。
// 本当は move_apply は rvalue references for *this を使って対応すべきなのでしょうが、
// gcc 4.5.0 にはまだ実装されていないので、泣く泣く専用のメンバを用意しています。
// ただ、 move 時は専用の関数を使う、というのだと扱いにくいので、
// 統一したアクセスを可能にするために、自由関数 apply_in_place を提供しています。
// これは boost::in_place_factory でも普通に扱えるので、専らそちらを使うといいかと。
//
// Copyright (C) 2010 Takaya Saito (SubaruG)
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
//
#ifndef ETUDE_INCLUDED_IN_PLACE_FACTORY_
#define ETUDE_INCLUDED_IN_PLACE_FACTORY_
#include <boost/utility/in_place_factory.hpp>
#include <utility>
#include <tuple>
#include <new>
#include <type_traits>
// preprocessor 周り
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
// 設定
#ifndef ETUDE_MAX_ARGS
#define ETUDE_MAX_ARGS 10
#endif
namespace etude {
using boost::in_place_factory_base;
// ヘルパメタ関数
template<class T>
struct is_in_place_factory
: std::is_base_of<in_place_factory_base, T> {};
template<class... Args>
struct in_place_factory;
template<>
struct in_place_factory<>
: in_place_factory_base, private std::tuple<>
{
template<class...Args>
friend class in_place_factory;
typedef std::tuple<> arguments;
explicit in_place_factory() {}
in_place_factory( std::tuple<> ) {}
template<class T>
T* apply( void* addr ) const {
return ::new(addr) T();
}
template<class T>
T* move_apply( void* addr ) {
return ::new(addr) T();
}
arguments const& get_arguments() const {
return *this;
}
arguments&& move_arguments() {
return static_cast<arguments&&>(*this);
}
};
#define ETUDE_GEN_IN_PLACE_( z, n, d ) \
template<BOOST_PP_ENUM_PARAMS(n, class A)> \
struct in_place_factory<BOOST_PP_ENUM_PARAMS(n, A)> \
: in_place_factory_base \
{ \
typedef std::tuple<BOOST_PP_ENUM_PARAMS(n, A)> arguments; \
\
explicit in_place_factory( BOOST_PP_ENUM_BINARY_PARAMS( n, A, && a ) ) \
: x( BOOST_PP_ENUM( n, ETUDE_GEN_FORWARD_, _ ) ) {} \
\
in_place_factory( arguments const& t ) \
: x( t ) {} \
in_place_factory( arguments && t ) \
: x( std::move(t) ) {} \
template<class...Args> \
in_place_factory( in_place_factory<Args...> const& src ) \
: x( src.x ) {} \
template<class...Args> \
in_place_factory( in_place_factory<Args...> && src ) \
: x( std::move(src.x) ) {} \
\
template<class T> \
T* apply( void* addr ) const { \
return ::new (addr) T( BOOST_PP_ENUM( n, ETUDE_GEN_GET_X_, _ ) ); \
} \
template<class T> \
T* move_apply( void* addr ) { \
return ::new (addr) T( BOOST_PP_ENUM( n, ETUDE_GEN_MOVE_X_, _ ) ); \
} \
\
arguments const& get_arguments() const { return x; } \
arguments&& move_arguments() { return std::move(x); } \
\
private: \
arguments x; \
\
template<class...Args> \
friend class in_place_factory; \
}; \
/* ETUDE_GEN_IN_PLACE_ */
#define ETUDE_GEN_FORWARD_( z, n, d ) \
std::forward<BOOST_PP_CAT( A, n )>( BOOST_PP_CAT( a, n ) )
#define ETUDE_GEN_GET_X_( z, n, d ) \
std::get<n>(x)
#define ETUDE_GEN_MOVE_X_( z, n, d ) \
std::forward<BOOST_PP_CAT( A, n )>( std::get<n>(x) )
// コード生成
BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC(ETUDE_MAX_ARGS), ETUDE_GEN_IN_PLACE_, _ )
#undef ETUDE_GEN_IN_PLACE_
#undef ETUDE_GEN_FORWARD_
#undef ETUDE_GEN_GET_X_
#undef ETUDE_GEN_MOVE_X_
// helper function
// 一時オブジェクトを rvalue-reference として束縛
// auto を使って束縛されると危険
template<class... Args>
inline in_place_factory<Args&&...> in_place( Args&& ...args ) {
return in_place_factory<Args&&...>( std::forward<Args&&>(args)... );
}
// auto で束縛しても問題ない安全版。しかし明らかに処理は増える
// std::unique_ptr 等の move だけ可能な引数を使う場合にはこっちは使えません
template<class... Args>
inline in_place_factory<Args...> in_place_safe( Args&& ...args ) {
return in_place_factory<Args...>( std::forward<Args>(args)... );
}
// 戻り値と move 対応を盛り込んだヘルパ関数
// boost 対応
template<class T, class InPlace,
typename = typename std::enable_if<is_in_place_factory<InPlace>::value>::type>
inline T* apply_in_place( InPlace const& x, void* addr ) {
x.template apply<T>( addr );
return static_cast<T*>( addr );
}
// etude::in_place_factory 版
// const 参照
template<class T, class... Args>
inline T* apply_in_place( in_place_factory<Args...> const& x, void* addr ) {
return x.template apply<T>( addr );
}
// move
template<class T, class... Args>
inline T* apply_in_place( in_place_factory<Args...> && x, void* addr ) {
return x.template move_apply<T>( addr );
}
}
#endif // #ifndef ETUDE_INCLUDED_IN_PLACE_FACTORY_
//
// typed_in_place_factory:
// Boost.TypedInPlaceFactory の C++0x 拡張
//
// 基本的には etude::in_place_factory と同じです。
// ファクトリ自身に型が付いてるので、オブジェクトのプロキシのように使えます。
// また、対応する etude::in_place_factory から暗黙変換できるのも特徴。
//
// Copyright (C) 2010 Takaya Saito (SubaruG)
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
//
#ifndef ETUDE_INCLUDED_TYPED_IN_PLACE_FACTORY_
#define ETUDE_INCLUDED_TYPED_IN_PLACE_FACTORY_
#include "in_place_factory.hpp"
#include <boost/utility/typed_in_place_factory.hpp>
#include <utility>
#include <new>
#include <type_traits>
namespace etude {
using boost::typed_in_place_factory_base;
// ヘルパメタ関数
template<class T>
struct is_typed_in_place_factory
: std::is_base_of<typed_in_place_factory_base, T> {};
template<class T, class... Args>
class typed_in_place_factory
: public typed_in_place_factory_base
{
typedef in_place_factory<Args...> impl_t;
impl_t impl_;
template<class U, class... As>
friend class typed_in_place_factory;
public:
typedef T value_type;
typedef typename impl_t::arguments arguments;
// 構築
explicit typed_in_place_factory( Args&& ...args )
: impl_( std::forward<Args>(args)... ) {}
// タプル、 in_place_factory からの構築
template<class U,
class = typename std::enable_if<std::is_convertible<U, impl_t>::value>::type >
typed_in_place_factory( U && x )
: impl_( x ) {}
// 他の typed_in_place_factory からの構築
// 構築するオブジェクトの型は同じじゃないと意味論的におかしい
template<class... As>
typed_in_place_factory( typed_in_place_factory<T, As...> const& x )
: impl_( x.impl_ ) {}
template<class... As>
typed_in_place_factory( typed_in_place_factory<T, As...> && x )
: impl_( std::move(x.impl_) ) {}
// オブジェクト構築
T* apply( void* addr ) const {
return impl_.template apply<T>(addr);
}
T* move_apply( void* addr ) {
return impl_.template move_apply<T>(addr);
}
// 情報取得
arguments const& get_arguments() const { return impl_.get_arguments(); }
arguments&& move_arguments() { return impl_.move_arguments(); }
};
// helper function
// 一時オブジェクトを rvalue-reference として束縛
// auto を使って束縛されると危険
template<class T, class... Args>
inline typed_in_place_factory<T, Args&&...> in_place( Args&& ...args ) {
return typed_in_place_factory<T, Args&&...>( std::forward<Args&&>(args)... );
}
// 安全版。しかし明らかに処理は増える
// std::unique_ptr を使う場合にはこっちは使えません
template<class T, class... Args>
inline typed_in_place_factory<T, Args...> in_place_safe( Args&& ...args ) {
return typed_in_place_factory<T, Args...>( std::forward<Args>(args)... );
}
// 戻り値と move 対応を盛り込んだヘルパ関数
// boost 対応
template<class InPlace,
typename T = typename std::enable_if<
is_typed_in_place_factory<InPlace>::value, typename InPlace::value_type
>::type
>
inline T* apply_in_place( InPlace const& x, void* addr ) {
x.apply( addr );
return static_cast<T*>( addr );
}
// etude::in_place_factory 版
// const 参照
template<class T, class... Args>
inline T* apply_in_place( typed_in_place_factory<T, Args...> const& x, void* addr ) {
return x.apply( addr );
}
// move
template<class T, class... Args>
inline T* apply_in_place( typed_in_place_factory<T, Args...> && x, void* addr ) {
return x.move_apply( addr );
}
}
#endif // #ifndef ETUDE_INCLUDED_TYPED_IN_PLACE_FACTORY_
@gintenlabo
Copy link
Author

これのインターフェイス改良版が http://github.com/gintenlabo/etude にあります。
こちらはもう更新しないつもりなので、よろしくです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment