// Copyright Louis Dionne 2013-2017 // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include namespace hana = boost::hana; //! [setup] template struct print_impl { template static void apply(std::ostream&, X const&) { // possibly some default implementation } }; template void print(std::ostream& os, X x) { using Tag = typename hana::tag_of::type; print_impl::apply(os, x); } //! [setup] //! [vector] struct vector_tag; struct vector0 { using hana_tag = vector_tag; static constexpr std::size_t size = 0; }; template struct vector1 { T1 t1; using hana_tag = vector_tag; static constexpr std::size_t size = 1; template auto const& operator[](Index i) const { static_assert(i == 0u, "index out of bounds"); return t1; } }; template struct vector2 { T1 t1; T2 t2; using hana_tag = vector_tag; static constexpr std::size_t size = 2; // Using Hana as a backend to simplify the example. template auto const& operator[](Index i) const { return *hana::make_tuple(&t1, &t2)[i]; } }; // and so on... //! [vector] //! [customize] template <> struct print_impl { template static void apply(std::ostream& os, vectorN xs) { auto N = hana::size_c; os << "["; N.times.with_index([&](auto i) { os << xs[i]; if (i != N - hana::size_c<1>) os << ", "; }); os << "]"; } }; //! [customize] #if 0 //! [customize-when] template struct print_impl> { template static void apply(std::ostream& os, Seq xs) { // Some implementation for any sequence } }; //! [customize-when] #endif int main() { { std::stringstream ss; vector0 v0; print(ss, v0); BOOST_HANA_RUNTIME_CHECK(ss.str() == "[]"); } { std::stringstream ss; vector1 v1{1}; print(ss, v1); BOOST_HANA_RUNTIME_CHECK(ss.str() == "[1]"); } { std::stringstream ss; vector2 v2{1, 'x'}; print(ss, v2); BOOST_HANA_RUNTIME_CHECK(ss.str() == "[1, x]"); } } namespace old_way { //! [old_way] void print(std::ostream& os, vector0) { os << "[]"; } template void print(std::ostream& os, vector1 v) { os << "[" << v.t1 << "]"; } template void print(std::ostream& os, vector2 v) { os << "[" << v.t1 << ", " << v.t2 << "]"; } // and so on... //! [old_way] } namespace preconditions { //! [preconditions] template void print(std::ostream& os, X x) { // **** check some precondition **** // The precondition only has to be checked here; implementations // can assume their arguments to always be sane. using Tag = typename hana::tag_of::type; print_impl::apply(os, x); } //! [preconditions] } namespace function_objects { //! [function_objects] // Defining a function object is only needed once and implementations do not // have to worry about static initialization and other painful tricks. struct print_t { template void operator()(std::ostream& os, X x) const { using Tag = typename hana::tag_of::type; print_impl::apply(os, x); } }; constexpr print_t print{}; //! [function_objects] static_assert(sizeof(print) || true, "remove unused variable print warning"); }