text: // Copyright (C) 2007  Davis E. King (davis@dlib.net)
  // License: Boost Software License   See LICENSE.txt for the full license.
  #ifndef DLIB_TUPLe_H_
  #define DLIB_TUPLe_H_
  
  #include "../enable_if.h"
  #include "../algs.h"
  #include "../serialize.h"
  #include "tuple_abstract.h"
  
  // ----------------------------------------------------------------------------------------
  
  #define DLIB_TUPLE_GLOBAL_HELPERS(N)                                                \
      DLIB_TUPLE_GH(N##0) DLIB_TUPLE_GH(N##1) DLIB_TUPLE_GH(N##2) DLIB_TUPLE_GH(N##3) \
      DLIB_TUPLE_GH(N##4) DLIB_TUPLE_GH(N##5) DLIB_TUPLE_GH(N##6) DLIB_TUPLE_GH(N##7) 
  
  #define DLIB_TUPLE_GH(N)  DLIB_TUPLE_GET_INDEX(N)  DLIB_TUPLE_GET_ITEM(N) DLIB_TUPLE_GET_HELPER_STRUCT(N)
  
  #define DLIB_TUPLE_MEMBER_GET(N)                                                    \
      DLIB_TUPLE_MG(N##0) DLIB_TUPLE_MG(N##1) DLIB_TUPLE_MG(N##2) DLIB_TUPLE_MG(N##3) \
      DLIB_TUPLE_MG(N##4) DLIB_TUPLE_MG(N##5) DLIB_TUPLE_MG(N##6) DLIB_TUPLE_MG(N##7) 
  
  #define DLIB_TUPLE_GET_INDEX(N) \
      template <class Q, class T> const typename enable_if<is_same_type<typename T::type##N,Q>, long>::type get_index (const T&) {return N;}
  
  #define DLIB_TUPLE_GET_ITEM(N) \
      template <class Q,class T> const typename enable_if<is_same_type<typename T::type##N,Q>,Q>::type& get_item_const (const T& t) {return t.v##N;}\
      template <class Q,class T>       typename enable_if<is_same_type<typename T::type##N,Q>,Q>::type& get_item (      T& t) {return t.v##N;}
  
  
  #define DLIB_TUPLE_GET_HELPER_STRUCT(N)                         \
      template <class T> struct get_helper<N,T>                   \
      {                                                           \
          typedef typename T::type##N type;                       \
          static const type& get(const T& t) { return t.v##N; }   \
          static       type& get( T& t) { return t.v##N; }        \
      };  
  
  #define DLIB_TUPLE_TEMPLATE_LIST(N) \
      class T##N##0 = null_type, class T##N##1 = null_type, class T##N##2 = null_type, class T##N##3 = null_type, \
      class T##N##4 = null_type, class T##N##5 = null_type, class T##N##6 = null_type, class T##N##7 = null_type
  
  #define DLIB_TUPLE_VARIABLE_LIST(N) \
      T##N##0 v##N##0; T##N##1 v##N##1; T##N##2 v##N##2; T##N##3 v##N##3; \
      T##N##4 v##N##4; T##N##5 v##N##5; T##N##6 v##N##6; T##N##7 v##N##7;  \
      typedef T##N##0 type##N##0; typedef T##N##1 type##N##1; typedef T##N##2 type##N##2;  \
      typedef T##N##3 type##N##3; typedef T##N##4 type##N##4; typedef T##N##5 type##N##5;  \
      typedef T##N##6 type##N##6; typedef T##N##7 type##N##7; 
  
  // ----------------------------------------------------------------------------------------
  
  namespace dlib
  {
  
      struct null_type{};
  
      // provide default serialization for the null_type
      inline void serialize (
          const null_type& ,
          std::ostream&
      ){}
      inline void deserialize (
          null_type& ,
          std::istream&
      ){}
  
  // ----------------------------------------------------------------------------------------
  
      namespace tuple_helpers
      {
          template <long idx, class T> struct get_helper;
  
          // use these preprocessor macros to declare all the global stuff used by the
          // tuple member functions.
          DLIB_TUPLE_GLOBAL_HELPERS(0)
          DLIB_TUPLE_GLOBAL_HELPERS(01)
          DLIB_TUPLE_GLOBAL_HELPERS(02)
          DLIB_TUPLE_GLOBAL_HELPERS(03)
  
      // ------------------------------------------------------------------------------------
  
      // use templates to recursively enumerate everything in the tuple that isn't a null_type
          template <
              typename T,
              typename F,
              long i = 0,
              typename enabled = void 
              >
          struct for_each 
          {
              static void go(
                  T& a, 
                  F& funct
              ) 
              {
                  funct(a.template get<i>());
                  for_each<T,F,i+1>::go(a,funct);
              }
  
              static bool go(
                  T& a, 
                  F& funct,
                  long idx
              ) 
              /*!
                  ensures
                      - returns true if the function was applied to the given index
                      - returns false if the index is invalid so the function wasn't 
                        applied to anything
              !*/
              {
                  if (idx == i)
                  {
                      funct(a.template get<i>());
                      return true;
                  }
                  else
                  {
                      return for_each<T,F,i+1>::go(a,funct,idx);
                  }
              }
          };
  
          template <bool v1, bool v2> struct template_or { const static bool value = true; };
          template <> struct template_or<false,false> { const static bool value = false; };
  
          // the base case of the recursion
          template <
              typename T,
              typename F,
              long i
              >
          struct for_each<T,F,i,typename enable_if<template_or<i == T::max_fields , is_same_type<null_type,typename T::template get_type<i>::type >::value> >::type >
          { 
              static void go( T&, F& ) { } 
              static bool go( T&, F&, long ) { return false; } 
          };
  
      // ------------------------------------------------------------------------------------
  
      // use templates to recursively enumerate everything in the tuple that isn't a null_type
          template <
              typename T,
              long i = 0,
              typename enabled = void 
              >
          struct tuple_swap 
          {
              static void go(
                  T& a,
                  T& b 
              ) 
              {
                  exchange(a.template get<i>(), b.template get<i>());
                  tuple_swap<T,i+1>::go(a,b);
              }
          };
  
          template <typename T, long i>
          struct at_base_case
          {
  
          };
  
          // the base case of the recursion
          template <
              typename T,
              long i
              >
          struct tuple_swap<T,i,typename enable_if<template_or<i == T::max_fields, is_same_type<null_type,typename T::template get_type<i>::type >::value > >::type >
          { static void go( T&, T& ) { } };
  
      // ------------------------------------------------------------------------------------
  
          struct tuple_serialize
          {
              tuple_serialize (std::ostream& out_) : out(out_){}
              std::ostream& out;
  
              template <typename T>
              void operator() (
                  T& a
              ) const { serialize(a,out); }
          };
  
      // ------------------------------------------------------------------------------------
  
          struct tuple_deserialize
          {
              tuple_deserialize (std::istream& in_) : in(in_){}
              std::istream& in; 
              template <typename T>
              void operator() (
                  T& a
              ) const { deserialize(a,in); }
          };
  
  
      }
  
  // ----------------------------------------------------------------------------------------
  
      // use these preprocessor macros to declare 4*8 template arguments (below we count them in octal)
      template < 
          DLIB_TUPLE_TEMPLATE_LIST(0),  // args 00-07
          DLIB_TUPLE_TEMPLATE_LIST(01), // args 010-017 
          DLIB_TUPLE_TEMPLATE_LIST(02), // args 020-027
          DLIB_TUPLE_TEMPLATE_LIST(03)  // args 030-037
          >
      class tuple
      {
      public:
  
          // use these macros to declare 8*4 member variables
          DLIB_TUPLE_VARIABLE_LIST(0)
          DLIB_TUPLE_VARIABLE_LIST(01)
          DLIB_TUPLE_VARIABLE_LIST(02)
          DLIB_TUPLE_VARIABLE_LIST(03)
  
          const static long max_fields = 4*8;
  
          template < long idx > 
          struct get_type 
          { 
              typedef typename tuple_helpers::get_helper<idx,tuple>::type type; 
          };
  
          template < long idx > 
          const typename tuple_helpers::get_helper<idx,tuple>::type& get (
          ) const { return tuple_helpers::get_helper<idx,tuple>::get(*this); }
  
          template < long idx >       
          typename tuple_helpers::get_helper<idx,tuple>::type& get (
          ) { return tuple_helpers::get_helper<idx,tuple>::get(*this); }
  
          template < class Q>  
          long index (
          ) const { return tuple_helpers::get_index<Q>(*this); }
  
          template <class Q>  
          const Q& get (
          ) const {return tuple_helpers::get_item_const<Q>(*this);}
  
          template <class Q> 
          Q& get (
          ) {return tuple_helpers::get_item<Q>(*this);}
  
  
  
  
          template <typename F>
          void for_index (
              F& funct,
              long idx
          ) 
          { 
              // do this #ifdef stuff to avoid getting a warning about valid_idx not being
              // used when ENABLE_ASSERTS isn't defined.
  #ifdef ENABLE_ASSERTS
              const bool valid_idx = tuple_helpers::for_each<tuple,F>::go(*this,funct,idx); 
  #else
              tuple_helpers::for_each<tuple,F>::go(*this,funct,idx); 
  #endif
              DLIB_ASSERT(valid_idx,
                  "\tvoid tuple::for_index()"
                  << "\n\tYou have attempted to call for_index() with an index out of the valid range"
                  << "\n\tidx:  " << idx 
                  << "\n\tthis: " << this
                  );
          }
  
          template <typename F>
          void for_index (
              F& funct,
              long idx
          ) const 
          { 
              // do this #ifdef stuff to avoid getting a warning about valid_idx not being
              // used when ENABLE_ASSERTS isn't defined.
  #ifdef ENABLE_ASSERTS
              const bool valid_idx = tuple_helpers::for_each<const tuple,F>::go(*this,funct,idx); 
  #else
              tuple_helpers::for_each<const tuple,F>::go(*this,funct,idx); 
  #endif
              DLIB_ASSERT(valid_idx,
                  "\tvoid tuple::for_index()"
                  << "\n\tYou have attempted to call for_index() with an index out of the valid range"
                  << "\n\tidx:  " << idx 
                  << "\n\tthis: " << this
                  );
          }
  
          template <typename F>
          void for_index (
              const F& funct,
              long idx
          ) 
          { 
              // do this #ifdef stuff to avoid getting a warning about valid_idx not being
              // used when ENABLE_ASSERTS isn't defined.
  #ifdef ENABLE_ASSERTS
              const bool valid_idx = tuple_helpers::for_each<tuple,const F>::go(*this,funct,idx); 
  #else
              tuple_helpers::for_each<tuple,const F>::go(*this,funct,idx); 
  #endif
              DLIB_ASSERT(valid_idx,
                  "\tvoid tuple::for_index()"
                  << "\n\tYou have attempted to call for_index() with an index out of the valid range"
                  << "\n\tidx:  " << idx 
                  << "\n\tthis: " << this
                  );
          }
  
          template <typename F>
          void for_index (
              const F& funct,
              long idx
          ) const
          { 
              // do this #ifdef stuff to avoid getting a warning about valid_idx not being
              // used when ENABLE_ASSERTS isn't defined.
  #ifdef ENABLE_ASSERTS
              const bool valid_idx = tuple_helpers::for_each<const tuple,const F>::go(*this,funct,idx); 
  #else
              tuple_helpers::for_each<const tuple,const F>::go(*this,funct,idx); 
  #endif
              DLIB_ASSERT(valid_idx,
                  "\tvoid tuple::for_index()"
                  << "\n\tYou have attempted to call for_index() with an index out of the valid range"
                  << "\n\tidx:  " << idx 
                  << "\n\tthis: " << this
                  );
          }
  
  
  
  
          template <typename F>
          void for_each (
              F& funct
          ) { tuple_helpers::for_each<tuple,F>::go(*this,funct); }
  
          template <typename F>
          void for_each (
              F& funct
          ) const { tuple_helpers::for_each<const tuple,F>::go(*this,funct); }
  
          template <typename F>
          void for_each (
              const F& funct
          ) const { tuple_helpers::for_each<const tuple,const F>::go(*this,funct); }
  
          template <typename F>
          void for_each (
              const F& funct
          ) { tuple_helpers::for_each<tuple,const F>::go(*this,funct); }
  
  
  
  
          inline friend void serialize (
              tuple& item,
              std::ostream& out
          )
          {
              try
              {
                  item.for_each(tuple_helpers::tuple_serialize(out));
              }
              catch (serialization_error& e)
              {
                  throw serialization_error(e.info + "\n   while serializing an object of type dlib::tuple<>");
              }
          }
  
          inline friend void deserialize (
              tuple& item,
              std::istream& in 
          )
          {
              try
              {
                  item.for_each(tuple_helpers::tuple_deserialize(in));
              }
              catch (serialization_error& e)
              {
                  throw serialization_error(e.info + "\n   while deserializing an object of type dlib::tuple<>");
              }
          }
  
          inline friend void swap (
              tuple& a,
              tuple& b
          )
          {
              tuple_helpers::tuple_swap<tuple>::go(a,b);
          }
  
          inline void swap(
              tuple& item
          ) { tuple_helpers::tuple_swap<tuple>::go(item,*this); }
  
      };
  
  // ----------------------------------------------------------------------------------------
  
  }
  
  #endif // DLIB_TUPLe_H_
  
  