/* Copyright (C) 2013 Tom Bachmann This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #ifndef CXX_TUPLE_H #define CXX_TUPLE_H #include "traits.h" namespace flint { /////////////////////////// // A general tuple class. /////////////////////////// // This simply stores a head and a tail. Conventionally, tuples a built by // nesting the tail. The last entry should be an empty_tuple (see below). // So e.g. a pair of integers would be tuple >. // // There are some helpers in the mp namespace below. namespace detail { struct FILLIT { }; } // detail namespace mp { namespace htuples { // Filling of tuples template Tuple fill(const Filler& f) { return Tuple(detail::FILLIT(), f); } } // htuples } // mp template struct tuple { Head head; Tail tail; typedef Head head_t; typedef Tail tail_t; static const unsigned len = 1 + Tail::len; // Obtain a reference to head (convenience name for pairs). typename traits::reference::type first() {return head;} typename traits::forwarding::type first() const {return head;} // Obtain a reference to the head of the tail (convenience name for pairs). typename traits::reference::type second() {return tail.head;} typename traits::forwarding::type second() const {return tail.head;} tuple() {}; tuple(typename traits::forwarding::type h, typename traits::forwarding::type t) : head(h), tail(t) { } bool operator==(const tuple& other) { return head == other.head && tail == other.tail; } template void set(const T& t) { head = t.head; tail.set(t.tail); } template void set(T& t) { head = t.head; tail.set(t.tail); } template bool equals_elementwise(const T& t) const { return head == t.head && tail.equals_elementwise(t.tail); } private: template tuple(detail::FILLIT fillit, const Filler& f) : head(f.template create()), tail(fillit, f) { } template friend Tuple mp::htuples::fill(const Filler&); template friend struct tuple; }; struct empty_tuple { struct empty { }; typedef empty head_t; typedef empty tail_t; empty head; empty tail; static const unsigned len = 0; bool operator==(const empty_tuple&) {return true;} empty_tuple() {} void set(empty_tuple) {} bool equals_elementwise(empty_tuple) const {return true;} private: template empty_tuple(detail::FILLIT fillit, const Filler& f) { } template friend Tuple mp::htuples::fill(const Filler&); template friend struct tuple; }; namespace detail { typedef void UNUSED; template struct maybe_forwarding { typedef typename traits::forwarding::type type; static type default_value(); }; template<> struct maybe_forwarding { typedef UNUSED* type; static type default_value() {return 0;} }; } namespace mp { // Helper to conveniently define tuple types, and marshall objects into // tuples. // Typical usage: // typedef make_tuple maker; // maker::type my_tuple = maker::make(1, 'a', WORD(2)); // TODO this would be a prime use for variadic templates ... #define FLINTXX_MAKE_TUPLE_TEMPLATE_ARGS \ class T1 = detail::UNUSED, class T2 = detail::UNUSED, \ class T3 = detail::UNUSED, class T4 = detail::UNUSED, \ class T5 = detail::UNUSED, class T6 = detail::UNUSED, \ class T7 = detail::UNUSED, class T8 = detail::UNUSED #define FLINTXX_MAKE_TUPLE_FUNC_ARGS \ typename detail::maybe_forwarding::type t1 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t2 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t3 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t4 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t5 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t6 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t7 \ = detail::maybe_forwarding::default_value(), \ typename detail::maybe_forwarding::type t8 \ = detail::maybe_forwarding::default_value() #define FLINTXX_MAKE_TUPLE_TYPES_APPLYMACRO(func) \ func(T1), func(T2), func(T3), func(T4), \ func(T5), func(T6), func(T7), func(T8) #define FLINTXX_MAKE_TUPLE_FUNC_ARG_NAMES t1, t2, t3, t4, t5, t6, t7, t8 template struct make_tuple { typedef make_tuple next; typedef typename next::type tail_t; typedef tuple type; static type make(FLINTXX_MAKE_TUPLE_FUNC_ARGS) { return type(t1, next::make(t2, t3, t4, t5, t6, t7, t8)); } }; template<> struct make_tuple { typedef detail::UNUSED* T; typedef empty_tuple type; // g++-4.4 bolts if we use (...), even though all arguments are PODs static empty_tuple make(T=0, T=0, T=0, T=0, T=0, T=0, T=0, T=0) {return empty_tuple();} }; // Indexified access template struct tuple_get { typedef tuple_get nget; typedef typename nget::type type; static typename traits::forwarding::type get(const Tuple& t) { return nget::get(t.tail); } static typename traits::reference::type get(Tuple& t) { return nget::get(t.tail); } }; template struct tuple_get { typedef typename Tuple::head_t type; static typename traits::forwarding::type get(const Tuple& t) { return t.head; } static typename traits::reference::type get(Tuple& t) { return t.head; } }; // Create a tuple backing a tuple of points. // That is to say, given a tuple like , // compute a backing tuple type . // // If one of the types in the tuple is Return*, do not back it // and instead feed it in separately. I.e. if Return is A*, then type // will be just . // // The static member init(to, from, ret) can be used to initalize the tuple // of pointers "to" to point to its backing in "from" and "ret". template struct back_tuple; // General case: non-empty tuple , and Return type cannot be // merged in. template struct back_tuple, Return> { typedef tuple::type> type; static void init(tuple& to, type& from, Return* ret = 0) { back_tuple::init(to.tail, from.tail, ret); to.head = &from.head; } }; // Merging case: non-empty tuple , and Return is Head* template struct back_tuple, Head> { typedef typename back_tuple::type type; static void init(tuple& to, type& from, Head* ret = 0) { to.head = ret; back_tuple::init(to.tail, from, 0 /* unused */ ); } }; // Base case: empty tuple; nothing to do. template struct back_tuple { typedef empty_tuple type; static void init(empty_tuple& to, type& from, Return* ret = 0) { } }; // Helper to concatenate two tuples. // // This has one member type, and three static member functions get_second, // get_first and doit. // "type" is a tuple type which can store the data of both Tuple1 and Tuple2. // Then, given an element of type "type", get_first and get_second can be used // to fill types Tuple1 and Tuple2. Note that get_second can return a constant // reference, whereas get_first has to do copying. // (But these copies should usually be inlined and optimized away by the // compiler.) // // Example: Tuple1 = , Tuple2 = . // Then type = // get_second(t) = t.tail.tail.tail // get_first(t) = tuple(t.head, tuple(t.tail.head, tuple( // t.tail.tail.head, empty_tuple()))); // template struct concat_tuple; // Degenerate case: Tuple1 is empty. template struct concat_tuple { typedef Tuple2 type; static const Tuple2& get_second(const Tuple2& t) {return t;} static empty_tuple get_first(const Tuple2& t) {return empty_tuple();} static type doit(const empty_tuple& t1, const Tuple2& t2) {return t2;} }; // General case: non-empty Tuple1. template struct concat_tuple, Tuple2> { typedef tuple::type> type; static const Tuple2& get_second(const type& t) { return concat_tuple::get_second(t.tail); } static tuple get_first(const type& o) { return tuple( o.head, concat_tuple::get_first(o.tail)); } static type doit(const tuple& t1, const Tuple2& t2) { return type(t1.head, concat_tuple::doit(t1.tail, t2)); } }; // Merging of tuples // // This takes two tuples, and computes a tuple type which can store either. // As usual, the extraction functions require copying which can be amortized // by the compiler. // template struct merge_tuple; //{ // typedef XYZ type; // Tuple1 get_first(const type& type); // Tuple2 get_second(const type& type); //}; // General case // NB: tail is *always* a sub-tuple of the second argument! template struct merge_tuple, Tuple2> { public: typedef merge_tuple comp1; typedef merge_tuple, typename comp1::tail_t> comp2; typedef concat_tuple concater; public: typedef typename concater::type type; // This is the part of type into which we can still merge. typedef typename comp2::tail_t tail_t; typedef typename concat_tuple< typename comp1::used_t, typename comp2::used_t >::type used_t; private: static typename comp1::type get_tail(const type& input) { typedef concat_tuple< typename comp1::used_t, typename comp1::tail_t > myconcat; return myconcat::doit(concater::get_first(input), comp2::get_second(concater::get_second(input))); } public: static tuple get_first(const type& input) { Head h = comp2::get_first(concater::get_second(input)).first(); return tuple(h, comp1::get_first(get_tail(input))); } static Tuple2 get_second(const type& input) { return comp1::get_second(get_tail(input)); } }; // First argument a singleton, no merging template struct merge_tuple, tuple > { private: typedef merge_tuple, Tail> comp; typedef concat_tuple< typename comp::used_t, tuple > concater; public: typedef typename comp::used_t used_t; typedef tuple tail_t; typedef typename concater::type type; private: static typename comp::type get_tail(const type& input) { typedef concat_tuple< typename comp::used_t, typename comp::tail_t > myconcat; return myconcat::doit(concater::get_first(input), concater::get_second(input).tail); } public: static tuple get_first(const type& input) { return comp::get_first(get_tail(input)); } static tuple get_second(const type& input) { return tuple( concater::get_second(input).head, comp::get_second(get_tail(input))); } }; // First argument a singleton, with merging template struct merge_tuple, tuple > { typedef tuple type; typedef tuple used_t; typedef Tail tail_t; static tuple get_first(const type& input) { return make_tuple::make(input.head); } static tuple get_second(const type& input) { return input; } }; // Termination case 1 template struct merge_tuple { typedef Tuple2 type; typedef type tail_t; typedef empty_tuple used_t; static empty_tuple get_first(const type& input) { return empty_tuple(); } static Tuple2 get_second(const type& input) { return input; } }; // It seems like this code path is unnecessary and in fact ambiguous. // I am fairly convinced by now this is correct. // However, in case issues ever come up, it seemed useful to me to retain this. // -- Tom Bachmann (15/10/2013) #if 0 // Termination case 2 template struct merge_tuple { typedef Tuple1 type; typedef type tail_t; typedef empty_tuple used_t; static Tuple1 get_first(const type& input) { return input; } static empty_tuple get_second(const type& input) { return empty_tuple(); } }; #endif // Termination case 3 template struct merge_tuple, empty_tuple> { typedef tuple type; typedef empty_tuple tail_t; // NB: we "use" T here - it cannot be merged into it any longer! typedef type used_t; static empty_tuple get_second(const type& input) { return empty_tuple(); } static tuple get_first(const type& input) { return input; } }; // Termination case 4 template<> struct merge_tuple { typedef empty_tuple type; typedef empty_tuple tail_t; typedef empty_tuple used_t; static empty_tuple get_first(const type& input) {return empty_tuple();} static empty_tuple get_second(const type& input) {return empty_tuple();} }; // Creation and manipulation of homogeneous tuples // Build a tuple type of "n" repetitions of "T". template struct make_homogeneous_tuple { typedef tuple::type> type; }; template struct make_homogeneous_tuple { typedef empty_tuple type; }; namespace htuples { namespace hdetail { template struct extract_helper { typedef typename Tuple::head_t T; typedef typename Tuple::tail_t tail_t; typedef typename make_homogeneous_tuple::type ht; static ht get_noskip(const Tuple& tuple) { return ht(tuple.head, extract_helper::get_noskip( tuple.tail)); } }; template struct extract_helper<0, Tuple> { static empty_tuple get_noskip(const Tuple&) {return empty_tuple();} }; template struct remove_helper { static const unsigned n = Tuple::len; typedef typename Tuple::tail_t tail_t; typedef typename Tuple::head_t T; static tail_t get(const Tuple& tuple, T res) { if(tuple.head == res) return tuple.tail; return tail_t(tuple.head, remove_helper::get(tuple.tail, res)); } }; template struct remove_helper > { static empty_tuple get(const tuple&, T) { return empty_tuple(); } }; } // hdetail // Extract the first "n" values from the *homogeneous* tuple "tuple". template inline typename make_homogeneous_tuple::type extract( const Tuple& tuple) { return hdetail::extract_helper::get_noskip(tuple); } // Remove one element from the *homogeneous* tuple "tuple", if possible "res". // Example: // t1 = (1, 2, 3, 4) // t2 = (1, 1, 1, 1) // t3 = (2, 3, 4, 5) // // removeres(t1, 1) -> (2, 3, 4) // removeres(t2, 1) -> (1, 1, 1) // removeres(t3, 1) -> (2, 3, 4) or any other three element subset template inline typename Tuple::tail_t removeres(const Tuple& tuple, typename Tuple::head_t res) { return hdetail::remove_helper::get(tuple, res); } } // htuples } // mp namespace traits { // Compute if "Tuple" is of the form (U, U, .. U), or empty. template struct is_homogeneous_tuple : mp::and_< is_homogeneous_tuple, mp::equal_types > { }; template struct is_homogeneous_tuple : true_ { }; // Compute if T is a tuple template struct is_tuple : false_ { }; template struct is_tuple > : true_ { }; template<> struct is_tuple : true_ { }; } } // flint #endif