base: Provide stl_helpers::operator<< for more types
This operator can be safely brought in scope when needed with "using stl_helpers::operator<<". 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. Change-Id: I1609dd6e85e25764f393458d736ec228e025da32 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67666 Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Tested-by: kokoro <noreply+kokoro@google.com> Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
This commit is contained in:
committed by
Bobby Bruce
parent
c634b23305
commit
73afee1e0d
@@ -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 <algorithm>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/compiler.hh"
|
||||
#include "base/stl_helpers/hash_helpers.hh"
|
||||
#include "base/stl_helpers/ostream_helpers.hh"
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
|
||||
namespace stl_helpers
|
||||
{
|
||||
|
||||
template <typename T, typename Enabled=void>
|
||||
struct IsHelpedContainer : public std::false_type {};
|
||||
|
||||
template <typename ...Types>
|
||||
struct IsHelpedContainer<std::vector<Types...>> : public std::true_type {};
|
||||
|
||||
template <typename ...Types>
|
||||
constexpr bool IsHelpedContainerV = IsHelpedContainer<Types...>::value;
|
||||
|
||||
/**
|
||||
* Write out all elements in an stl container as a space separated
|
||||
* list enclosed in square brackets
|
||||
*
|
||||
* @ingroup api_base_utils
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<IsHelpedContainerV<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
|
||||
|
||||
@@ -26,3 +26,4 @@
|
||||
Import('*')
|
||||
|
||||
GTest('hash_helpers.test', 'hash_helpers.test.cc')
|
||||
GTest('ostream_helpers.test', 'ostream_helpers.test.cc')
|
||||
|
||||
192
src/base/stl_helpers/ostream_helpers.hh
Normal file
192
src/base/stl_helpers/ostream_helpers.hh
Normal file
@@ -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 <iostream>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#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<typename T>
|
||||
std::ostream&
|
||||
opExtractSecDisp(std::ostream& os, const T& v);
|
||||
|
||||
template <typename E>
|
||||
std::enable_if_t<std::is_enum_v<E>,
|
||||
std::ostream&>
|
||||
opExtractPrimDisp(std::ostream& os, const E& e)
|
||||
{
|
||||
return os << magic_enum::enum_name(e);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, const std::tuple<T...>& p)
|
||||
{
|
||||
std::apply([&](auto&&... e) {
|
||||
std::size_t n{0};
|
||||
os << '(';
|
||||
((opExtractSecDisp(os, e) << (++n != sizeof...(T) ? ", " : "")), ...);
|
||||
os << ')';
|
||||
}, p);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, const std::pair<T, U>& p)
|
||||
{
|
||||
return opExtractPrimDisp(os, std::tie(p.first, p.second));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<is_iterable_v<T>, std::ostream&>
|
||||
opExtractPrimDisp(std::ostream& os, const T& v)
|
||||
{
|
||||
os << "[ ";
|
||||
for (auto& e: v) {
|
||||
opExtractSecDisp(os, e) << ", ";
|
||||
}
|
||||
return os << ']';
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, const std::optional<T>& o)
|
||||
{
|
||||
if (o) {
|
||||
return opExtractSecDisp(os, *o);
|
||||
} else {
|
||||
return os << '-';
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, T* p);
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, const std::shared_ptr<T>& p)
|
||||
{
|
||||
return opExtractPrimDisp(os, p.get());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, const std::unique_ptr<T>& p)
|
||||
{
|
||||
return opExtractPrimDisp(os, p.get());
|
||||
}
|
||||
|
||||
template <typename, typename = void>
|
||||
constexpr bool isOpExtractNativelySupported = false;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool isOpExtractNativelySupported<T,
|
||||
std::void_t<decltype(
|
||||
std::declval<std::ostream&>() << std::declval<T>())>> = true;
|
||||
|
||||
template <typename, typename = void>
|
||||
constexpr bool isOpExtractHelped = false;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool isOpExtractHelped<T,
|
||||
std::void_t<decltype(
|
||||
opExtractPrimDisp(std::declval<std::ostream&>(),
|
||||
std::declval<T>()))>>
|
||||
= true;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool needsDispatch =
|
||||
isOpExtractHelped<T> && !isOpExtractNativelySupported<T>;
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
opExtractPrimDisp(std::ostream& os, T* p)
|
||||
{
|
||||
if (!p) {
|
||||
return os << "nullptr";
|
||||
}
|
||||
if constexpr (isOpExtractHelped<T> || isOpExtractNativelySupported<T>) {
|
||||
os << '(' << p << ": ";
|
||||
opExtractSecDisp(os, *p);
|
||||
return os << ')';
|
||||
} else {
|
||||
return os << p;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream&
|
||||
opExtractSecDisp(std::ostream& os, const T& v)
|
||||
{
|
||||
if constexpr (needsDispatch<T>) {
|
||||
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<typename T>
|
||||
std::enable_if_t<opExtract_impl::needsDispatch<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
|
||||
66
src/base/stl_helpers/ostream_helpers.test.cc
Normal file
66
src/base/stl_helpers/ostream_helpers.test.cc
Normal file
@@ -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 <gtest/gtest.h>
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#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<const char*>{"abc", "defg", "hijklm", "\n"};
|
||||
std::ostringstream os;
|
||||
os << v;
|
||||
EXPECT_EQ(os.str(), "[ abc, defg, hijklm, \n, ]");
|
||||
}
|
||||
|
||||
TEST(OstreamHelpers, map) {
|
||||
auto m = std::map<char, int>{{'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), ]");
|
||||
}
|
||||
Reference in New Issue
Block a user