diff --git a/src/base/stl_helpers.hh b/src/base/stl_helpers.hh index 1d19f56676..b70eea9992 100644 --- a/src/base/stl_helpers.hh +++ b/src/base/stl_helpers.hh @@ -26,59 +26,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BASE_STL_HELPERS_HH__ -#define __BASE_STL_HELPERS_HH__ +#ifndef BASE_STL_HELPERS_HH +#define BASE_STL_HELPERS_HH -#include -#include -#include -#include -#include - -#include "base/compiler.hh" #include "base/stl_helpers/hash_helpers.hh" +#include "base/stl_helpers/ostream_helpers.hh" -namespace gem5 -{ - -namespace stl_helpers -{ - -template -struct IsHelpedContainer : public std::false_type {}; - -template -struct IsHelpedContainer> : public std::true_type {}; - -template -constexpr bool IsHelpedContainerV = IsHelpedContainer::value; - -/** - * Write out all elements in an stl container as a space separated - * list enclosed in square brackets - * - * @ingroup api_base_utils - */ - -template -std::enable_if_t, std::ostream &> -operator<<(std::ostream& out, const T &t) -{ - out << "[ "; - bool first = true; - auto printer = [&first, &out](const auto &elem) { - if (first) - out << elem; - else - out << " " << elem; - }; - std::for_each(t.begin(), t.end(), printer); - out << " ]"; - out << std::flush; - return out; -} - -} // namespace stl_helpers -} // namespace gem5 - -#endif // __BASE_STL_HELPERS_HH__ +#endif // BASE_STL_HELPERS_HH diff --git a/src/base/stl_helpers/SConscript b/src/base/stl_helpers/SConscript index 1143dc2d42..7328cf066a 100644 --- a/src/base/stl_helpers/SConscript +++ b/src/base/stl_helpers/SConscript @@ -26,3 +26,4 @@ Import('*') GTest('hash_helpers.test', 'hash_helpers.test.cc') +GTest('ostream_helpers.test', 'ostream_helpers.test.cc') diff --git a/src/base/stl_helpers/ostream_helpers.hh b/src/base/stl_helpers/ostream_helpers.hh new file mode 100644 index 0000000000..a1c92e1ed1 --- /dev/null +++ b/src/base/stl_helpers/ostream_helpers.hh @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_STL_HELPERS_OSTREAM_HELPERS_HH +#define BASE_STL_HELPERS_OSTREAM_HELPERS_HH + +#include +#include +#include +#include + +#include "base/type_traits.hh" +#include "magic_enum/magic_enum.hh" + +namespace gem5::stl_helpers +{ + +namespace opExtract_impl +{ + +/* + * In order to provide a specialization for operator<< with stl_helpers-enabled + * types + * without loosing the hability to use it with other types, a dual-dispatch + * mechanism is used. The only entry point in the system is through a primary + * dispatch function that won't resolve for non-helped types. Then, recursive + * calls go through the secondary dispatch interface that sort between helped + * and non-helped types. Helped typed will enter the system back through the + * primary dispatch interface while other types will look for operator<< + * through regular lookup, especially ADL. + */ + +template +std::ostream& +opExtractSecDisp(std::ostream& os, const T& v); + +template +std::enable_if_t, +std::ostream&> +opExtractPrimDisp(std::ostream& os, const E& e) +{ + return os << magic_enum::enum_name(e); +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::tuple& p) +{ + std::apply([&](auto&&... e) { + std::size_t n{0}; + os << '('; + ((opExtractSecDisp(os, e) << (++n != sizeof...(T) ? ", " : "")), ...); + os << ')'; + }, p); + return os; +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::pair& p) +{ + return opExtractPrimDisp(os, std::tie(p.first, p.second)); +} + +template +std::enable_if_t, std::ostream&> +opExtractPrimDisp(std::ostream& os, const T& v) +{ + os << "[ "; + for (auto& e: v) { + opExtractSecDisp(os, e) << ", "; + } + return os << ']'; +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::optional& o) +{ + if (o) { + return opExtractSecDisp(os, *o); + } else { + return os << '-'; + } +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, T* p); + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::shared_ptr& p) +{ + return opExtractPrimDisp(os, p.get()); +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::unique_ptr& p) +{ + return opExtractPrimDisp(os, p.get()); +} + +template +constexpr bool isOpExtractNativelySupported = false; + +template +constexpr bool isOpExtractNativelySupported() << std::declval())>> = true; + +template +constexpr bool isOpExtractHelped = false; + +template +constexpr bool isOpExtractHelped(), + std::declval()))>> + = true; + +template +constexpr bool needsDispatch = + isOpExtractHelped && !isOpExtractNativelySupported; + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, T* p) +{ + if (!p) { + return os << "nullptr"; + } + if constexpr (isOpExtractHelped || isOpExtractNativelySupported) { + os << '(' << p << ": "; + opExtractSecDisp(os, *p); + return os << ')'; + } else { + return os << p; + } +} + +template +std::ostream& +opExtractSecDisp(std::ostream& os, const T& v) +{ + if constexpr (needsDispatch) { + return opExtractPrimDisp(os, v); + } else { + return os << v; + } +} + +} // namespace opExtract_impl + +// Add "using stl_helpers::operator<<" in the scope where you want to use it. +template +std::enable_if_t, std::ostream&> +operator<<(std::ostream& os, const T& v) +{ + return opExtract_impl::opExtractPrimDisp(os, v); +} + +} // namespace gem5::stl_helpers + +#endif // BASE_STL_HELPERS_OSTREAM_HELPERS_HH diff --git a/src/base/stl_helpers/ostream_helpers.test.cc b/src/base/stl_helpers/ostream_helpers.test.cc new file mode 100644 index 0000000000..19a1ece27e --- /dev/null +++ b/src/base/stl_helpers/ostream_helpers.test.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer; redistributions in binary + * form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials + * provided with the distribution; neither the name of the copyright holders + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include "base/stl_helpers/ostream_helpers.hh" + +using gem5::stl_helpers::operator<<; + +TEST(OstreamHelpers, pair) { + auto p = std::make_pair(1, 2); + std::ostringstream os; + os << p; + EXPECT_EQ(os.str(), "(1, 2)"); +} + +TEST(OstreamHelpers, tuple) { + auto t = std::make_tuple(true, + std::make_pair("Hello", std::string_view("World")), '!'); + std::ostringstream os; + os << t; + EXPECT_EQ(os.str(), "(1, (Hello, World), !)"); +} + +TEST(OstreamHelpers, vector) { + auto v = std::vector{"abc", "defg", "hijklm", "\n"}; + std::ostringstream os; + os << v; + EXPECT_EQ(os.str(), "[ abc, defg, hijklm, \n, ]"); +} + +TEST(OstreamHelpers, map) { + auto m = std::map{{'a', 0}, {'b', 1}, {'c', 2}, {'d', 3}}; + std::ostringstream os; + os << m; + EXPECT_EQ(os.str(), "[ (a, 0), (b, 1), (c, 2), (d, 3), ]"); +}