//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_INTERPROCESS_NAMED_PROXY_HPP
#define BOOST_INTERPROCESS_NAMED_PROXY_HPP

#ifndef BOOST_CONFIG_HPP
#  include <boost/config.hpp>
#endif
#
#if defined(BOOST_HAS_PRAGMA_ONCE)
#  pragma once
#endif

#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/workaround.hpp>

// interprocess/detail
#include <boost/interprocess/detail/mpl.hpp>
#include <boost/move/utility_core.hpp>
#ifndef BOOST_INTERPROCESS_PERFECT_FORWARDING
#include <boost/move/detail/fwd_macros.hpp>
#else
#include <boost/move/utility_core.hpp>
#include <boost/interprocess/detail/variadic_templates_tools.hpp>
#endif   //#ifdef BOOST_INTERPROCESS_PERFECT_FORWARDING

#include <boost/container/uses_allocator_construction.hpp>
#include <boost/container/detail/placement_new.hpp>
#include <boost/interprocess/allocators/detail/allocator_common.hpp>

#include <cstddef>

//!\file
//!Describes a proxy class that implements named allocation syntax.

namespace boost {
namespace interprocess {
namespace ipcdetail {

template<class T>
inline void named_construct_placement_destroy(void *mem, std::size_t num)
{
   T* memory = static_cast<T*>(mem); \
   for(std::size_t destroyed = 0; destroyed < num; ++destroyed)
      (memory++)->~T();
}


#ifdef BOOST_INTERPROCESS_PERFECT_FORWARDING

template<class T, bool is_iterator, class ...Args>
struct CtorArgN
{
   typedef T object_type;
   typedef bool_<is_iterator> IsIterator;
   typedef CtorArgN<T, is_iterator, Args...> self_t;
   typedef typename build_number_seq<sizeof...(Args)>::type index_tuple_t;

   self_t& operator++()
   {
      this->do_increment(IsIterator(), index_tuple_t());
      return *this;
   }

   self_t  operator++(int) {  return ++*this;   *this;  }

   CtorArgN(Args && ...args)
      :  args_(args...)
   {}

   template<class SegmentManager>
   void construct_n(void *mem, SegmentManager *segment_manager, std::size_t num)
   {
      std::size_t constructed = 0;
      BOOST_INTERPROCESS_TRY{
         T* memory      = static_cast<T*>(mem);
         for(constructed = 0; constructed < num; ++constructed){
            this->construct(memory++, segment_manager, IsIterator(), index_tuple_t());
            this->do_increment(IsIterator(), index_tuple_t());
         }
      }
      BOOST_INTERPROCESS_CATCH(...) {
         named_construct_placement_destroy<T>(mem, constructed);
         BOOST_INTERPROCESS_RETHROW
      } BOOST_INTERPROCESS_CATCH_END
   }

   private:
   template<class SegmentManager, std::size_t ...IdxPack>
   void construct(void* mem, SegmentManager *segment_manager, true_, const index_tuple<IdxPack...>&)
   {
      typedef uses_segment_manager<SegmentManager> uses_segment_manager_t;
      boost::container::uninitialized_construct_using_allocator
         (static_cast<T*>(mem), uses_segment_manager_t(segment_manager), *boost::forward<Args>((get<IdxPack>)(args_))...);
   }

   template<class SegmentManager, std::size_t ...IdxPack>
   void construct(void *mem, SegmentManager *segment_manager, false_, const index_tuple<IdxPack...>&)
   {
      typedef uses_segment_manager<SegmentManager> uses_segment_manager_t;
      boost::container::uninitialized_construct_using_allocator
         (static_cast<T*>(mem), uses_segment_manager_t(segment_manager), boost::forward<Args>((get<IdxPack>)(args_))...);
   }
   
   template<std::size_t ...IdxPack>
   void do_increment(true_, const index_tuple<IdxPack...>&)
   {
      this->expansion_helper(++(get<IdxPack>)(args_)...);
   }

   template<class ...ExpansionArgs>
   void expansion_helper(ExpansionArgs &&...)
   {}

   template<std::size_t ...IdxPack>
   void do_increment(false_, const index_tuple<IdxPack...>&)
   {}

   tuple<Args&...> args_;
};

//!Describes a proxy class that implements named
//!allocation syntax.
template
   < class SegmentManager  //segment manager to construct the object
   , class T               //type of object to build
   , bool is_iterator      //passing parameters are normal object or iterators?
   >
class named_proxy
{
   typedef typename SegmentManager::char_type char_type;
   const char_type *    mp_name;
   SegmentManager *     mp_mngr;
   mutable std::size_t  m_num;
   const bool           m_find;
   const bool           m_dothrow;

   public:
   named_proxy(SegmentManager *mngr, const char_type *name, bool find, bool dothrow)
      :  mp_name(name), mp_mngr(mngr), m_num(1)
      ,  m_find(find),  m_dothrow(dothrow)
   {}

   template<class ...Args>
   T *operator()(Args &&...args) const
   {
      CtorArgN<T, is_iterator, Args...> &&ctor_obj = CtorArgN<T, is_iterator, Args...>
         (boost::forward<Args>(args)...);
      return mp_mngr->generic_construct(ctor_obj, mp_name, m_num, m_find, m_dothrow);
   }

   //This operator allows --> named_new("Name")[3]; <-- syntax
   const named_proxy &operator[](std::size_t num) const
   {  m_num *= num; return *this;  }
};

#else //#ifdef BOOST_INTERPROCESS_PERFECT_FORWARDING

#define BOOST_INTERPROCESS_NAMED_PROXY_CTORARGN(N)\
\
template<class T BOOST_MOVE_I##N BOOST_MOVE_CLASS##N >  \
struct CtorArg##N\
{\
   typedef T object_type;\
   typedef CtorArg##N self_t;\
   \
   CtorArg##N ( BOOST_MOVE_UREF##N  )\
      BOOST_MOVE_COLON##N BOOST_MOVE_FWD_INIT##N{}\
   \
   template<class SegmentManager>\
   void construct_n(void *mem, SegmentManager *segment_manager, std::size_t num)\
   {\
      std::size_t constructed = 0;\
      BOOST_INTERPROCESS_TRY{\
         T* memory = static_cast<T*>(mem);\
         for (constructed = 0; constructed < num; ++constructed) {\
            typedef uses_segment_manager<SegmentManager> uses_segment_manager_t;\
            boost::container::uninitialized_construct_using_allocator\
               (memory++, uses_segment_manager_t(segment_manager) BOOST_MOVE_I##N BOOST_MOVE_MFWD##N);\
         }\
      }\
      BOOST_INTERPROCESS_CATCH(...) {\
         named_construct_placement_destroy<T>(mem, constructed);\
         BOOST_INTERPROCESS_RETHROW\
      } BOOST_INTERPROCESS_CATCH_END\
   }\
   \
   private:\
   BOOST_MOVE_MREF##N\
};\
//!
BOOST_MOVE_ITERATE_0TO9(BOOST_INTERPROCESS_NAMED_PROXY_CTORARGN)
#undef BOOST_INTERPROCESS_NAMED_PROXY_CTORARGN

#define BOOST_INTERPROCESS_NAMED_PROXY_CTORITN(N)\
\
template<class T BOOST_MOVE_I##N BOOST_MOVE_CLASS##N > \
struct CtorIt##N\
{\
   typedef T object_type;\
   typedef CtorIt##N self_t;\
   \
   self_t& operator++()\
   {  BOOST_MOVE_MINC##N;  return *this;  }\
   \
   self_t  operator++(int) {  return ++*this; *this;  }\
   \
   CtorIt##N ( BOOST_MOVE_VAL##N  )\
      BOOST_MOVE_COLON##N BOOST_MOVE_VAL_INIT##N{}\
   \
   template<class SegmentManager>\
   void construct_n(void *mem, SegmentManager *segment_manager, std::size_t num)\
   {\
      std::size_t constructed = 0;\
      BOOST_INTERPROCESS_TRY{\
         T* memory      = static_cast<T*>(mem);\
         for(constructed = 0; constructed < num; ++constructed){\
            typedef uses_segment_manager<SegmentManager> uses_segment_manager_t;\
            boost::container::uninitialized_construct_using_allocator\
               (memory++, uses_segment_manager_t(segment_manager) BOOST_MOVE_I##N BOOST_MOVE_MITFWD##N);\
            ++(*this);\
         }\
      }\
      BOOST_INTERPROCESS_CATCH(...) {\
         named_construct_placement_destroy<T>(mem, constructed);\
         BOOST_INTERPROCESS_RETHROW\
      } BOOST_INTERPROCESS_CATCH_END\
   }\
   \
   private:\
   BOOST_MOVE_MEMB##N\
};\
//!
BOOST_MOVE_ITERATE_0TO9(BOOST_INTERPROCESS_NAMED_PROXY_CTORITN)
#undef BOOST_INTERPROCESS_NAMED_PROXY_CTORITN

//!Describes a proxy class that implements named
//!allocation syntax.
template
   < class SegmentManager  //segment manager to construct the object
   , class T               //type of object to build
   , bool is_iterator      //passing parameters are normal object or iterators?
   >
class named_proxy
{
   typedef T         object_type;
   typedef typename SegmentManager::char_type char_type;
   const char_type *    mp_name;
   SegmentManager *     mp_mngr;
   mutable std::size_t  m_num;
   const bool           m_find;
   const bool           m_dothrow;

   public:
   named_proxy(SegmentManager *mngr, const char_type *name, bool find, bool dothrow)
      :  mp_name(name), mp_mngr(mngr), m_num(1)
      ,  m_find(find),  m_dothrow(dothrow)
   {}

   #define BOOST_INTERPROCESS_NAMED_PROXY_CALL_OPERATOR(N)\
   \
   BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
   T *operator()( BOOST_MOVE_UREF##N ) const\
   {\
      typedef typename if_c<is_iterator \
         , CtorIt##N <T BOOST_MOVE_I##N BOOST_MOVE_TARG##N> \
         , CtorArg##N<T BOOST_MOVE_I##N BOOST_MOVE_TARG##N> \
         >::type ctor_obj_t;\
      ctor_obj_t ctor_obj = ctor_obj_t( BOOST_MOVE_FWD##N );\
      return mp_mngr->generic_construct(ctor_obj, mp_name, m_num, m_find, m_dothrow);\
   }\
   //
   BOOST_MOVE_ITERATE_0TO9(BOOST_INTERPROCESS_NAMED_PROXY_CALL_OPERATOR)
   #undef BOOST_INTERPROCESS_NAMED_PROXY_CALL_OPERATOR

   ////////////////////////////////////////////////////////////////////////
   //             What the macro should generate (n == 2)
   ////////////////////////////////////////////////////////////////////////
   //
   // template <class P1, class P2>
   // T *operator()(P1 &p1, P2 &p2) const
   // {
   //    typedef CtorArg2
   //       <T, is_iterator, P1, P2>
   //       ctor_obj_t;
   //    ctor_obj_t ctor_obj(p1, p2);
   //
   //    return mp_mngr->(ctor_obj, mp_name, m_num, m_find, m_dothrow);
   // }
   //
   //////////////////////////////////////////////////////////////////////////

   //This operator allows --> named_new("Name")[3]; <-- syntax
   const named_proxy &operator[](std::size_t num) const
      {  m_num *= num; return *this;  }
};

#endif   //#ifdef BOOST_INTERPROCESS_PERFECT_FORWARDING

}}}   //namespace boost { namespace interprocess { namespace ipcdetail {

#include <boost/interprocess/detail/config_end.hpp>

#endif //#ifndef BOOST_INTERPROCESS_NAMED_PROXY_HPP
