#pragma once #include namespace europa::util { template struct IntoImpl { constexpr static To DoInto(const From& from) { // The default implentation assumes that the To type has a constructor which // can initalize it from a value of From const&. return To(from); } }; /// Into() support for vectors of items. template struct IntoImpl, std::vector> { constexpr static std::vector DoInto(const std::vector& from) { // FIXME: Ranges could make this less stupid auto vec = std::vector {}; vec.resize(from.size()); // Feed into existing Into() implementation for(auto const& item : from) vec.push_back(Into(item)); return vec; } }; /// Allows a type to be converted. /// A user is allowed to supply conversions out-of-band. template constexpr To Into(const From& from) { return IntoImpl::DoInto(from); } /// Helper macro to hide away the annoying boilerplate of /// implementing the IntoImpl struct used for implementing conversion #define EUROPA_BEGIN_INTO_CONVERSION(To, From) \ template <> \ struct ::europa::util::IntoImpl { \ static To DoInto([[maybe_unused]] const From& from) #define EUROPA_END_INTO_CONVERSION } #if 0 // An example of specializing Into(). // This is a bit iffy, but works. struct Frob {}; EUROPA_BEGIN_INTO_CONVERSION(int, Frob) { return 42; } EUROPA_END_INTO_CONVERSION; auto a() { Frob f{}; int i = Into(f); } #endif } // namespace europa::util