Debugger: pmr containers

Added support for visualization of for `std::pmr` containers
(and more broadly containers with custom non-empty allocators).
The gotcha with pmr allocators in particular is that
unlike most allocators these are non-empty classes:
they contain a pointer to a memory resource.

Tested locally with CDB and GDB (MinGW).

Task number: QTCREATORBUG-30224

Change-Id: If0ceb8a46905151a98f27d3e3ff11386406a9737
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Andrii Semkiv
2024-11-06 16:48:11 +01:00
parent 73c5d4e5e5
commit cb23332ae5
4 changed files with 385 additions and 172 deletions

View File

@@ -53,7 +53,28 @@ def qdump__std____1__list(d, value):
def qdump__std____1__set(d, value):
(proxy, head, size) = value.split("ppp")
# Looking up the allocator template type is potentially an expensive operation.
# A better alternative would be finding out the allocator type through accessing a class member.
# However due to different implementations doing things very differently
# it is deemed acceptable.
# Specifically:
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
# and the sentinel node
# * Clang packs the allocator and the sentinel node into a compressed pair,
# so depending on whether the allocator is sized or not,
# there may or may not be a member to access
# * MSVC goes even one step further and stores the allocator and the sentinel node together
# in a compressed pair, which in turn is stored together with the comparator inside
# another compressed pair
alloc_type = value.type[2]
alloc_size = alloc_type.size()
# size of empty allocators (which are the majority) is reported as 1
# in theory there can be allocators whose size is truly 1 byte,
# in which case this will cause issues, but in practice they should be rather rare
if alloc_size > 1:
(proxy, head, alloc, size) = value.split(f'pp{{{alloc_type.name}}}p')
else:
(proxy, head, size) = value.split("ppp")
d.check(0 <= size and size <= 100 * 1000 * 1000)
d.putItemCount(size)

View File

@@ -1,6 +1,25 @@
# Copyright (C) 2016 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
# Disclaimers:
# 1. Looking up the allocator template type is potentially an expensive operation.
# A better alternative would be finding out the allocator type
# through accessing a class member.
# However due to different implementations doing things very differently
# it is deemed acceptable.
# Specifically:
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
# and the sentinel node
# * Clang packs the allocator and the sentinel node into a compressed pair,
# so depending on whether the allocator is sized or not,
# there may or may not be a member to access
# * MSVC goes even one step further and stores the allocator and the sentinel node together
# in a compressed pair, which in turn is stored together with the comparator inside
# another compressed pair
# 2. `size` on an empty type, which the majority of the allocators are of,
# for whatever reason reports 1. In theory there can be allocators whose type is truly 1 byte,
# in which case we will have issues, but in practice they should be rather rare.
from utils import DisplayFormat
from dumper import Children, SubItem, DumperBase
@@ -94,7 +113,13 @@ def qdumpHelper__std__deque__libstdcxx(d, value):
pcur = pfirst
def qdumpHelper__std__deque__libcxx(d, value):
mptr, mfirst, mbegin, mend, start, size = value.split("pppptt")
alloc_type = value.type[1] # see disclaimer #1
alloc_size = alloc_type.size()
# see disclaimer #2
if alloc_size > 1:
mptr, mfirst, mbegin, mend, alloc, start, size = value.split(f'pppp{{{alloc_type.name}}}tt')
else:
mptr, mfirst, mbegin, mend, start, size = value.split("pppptt")
d.check(0 <= size and size <= 1000 * 1000 * 1000)
d.putItemCount(size)
if d.isExpanded():
@@ -160,7 +185,11 @@ def qdumpHelper__std__deque__msvc(d, value):
else:
bufsize = 1
(proxy, map, mapsize, myoff, mysize) = value.split("ppppp")
alloc_size = value.type[1].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
core = d.createValue(value.address() + offset, value.type)
(proxy, map, mapsize, myoff, mysize) = core.split("ppppp")
d.check(0 <= mapsize and mapsize <= 1000 * 1000 * 1000)
d.putItemCount(mysize)
@@ -221,10 +250,14 @@ def qdump__std__list__QNX(d, value):
d.isDebugBuild = True
except Exception:
d.isDebugBuild = False
alloc_size = value.type[1].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
(proxy, head, size) = value.split("ppp")
else:
(head, size) = value.split("pp")
offset += d.ptrSize() # _Myval2.Myproxy
core = d.createValue(value.address() + offset, value.type)
(head, size) = core.split("pp")
d.putItemCount(size, 1000)
@@ -331,10 +364,13 @@ def qdump_std__map__helper(d, value):
d.isDebugBuild = True
except Exception:
d.isDebugBuild = False
alloc_size = value.type[3].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
(proxy, head, size) = value.split("ppp")
else:
(head, size) = value.split("pp")
offset += d.ptrSize() # _Myval2.Myproxy
core = d.createValue(value.address() + offset, value.type)
(head, size) = core.split("pp")
d.check(0 <= size and size <= 100 * 1000 * 1000)
d.putItemCount(size)
if d.isExpanded():
@@ -559,10 +595,14 @@ def qdump__std__set__QNX(d, value):
d.isDebugBuild = True
except Exception:
d.isDebugBuild = False
alloc_size = value.type[2].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
(proxy, head, size) = value.split("ppp")
else:
(head, size) = value.split("pp")
offset += d.ptrSize() # _Myval2.Myproxy
core = d.createValue(value.address() + offset, value.type)
(head, size) = core.split("pp")
d.check(0 <= size and size <= 100 * 1000 * 1000)
d.putItemCount(size)
if d.isExpanded():
@@ -840,10 +880,14 @@ def qdump__std__unordered_map(d, value):
d.isDebugBuild = True
except Exception:
d.isDebugBuild = False
alloc_size = value.type[4].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
(_, start, size) = _list.split("ppp")
else:
(start, size) = _list.split("pp")
offset += d.ptrSize() # _Myval2.Myproxy
core = d.createValue(_list.address() + offset, _list.type)
(start, size) = core.split("pp")
else:
try:
# gcc ~= 4.7
@@ -1083,12 +1127,16 @@ def qdumpHelper__std__vector__msvc(d, value):
d.isDebugBuild = True
except RuntimeError:
d.isDebugBuild = False
alloc_size = value.type[1].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
proxy1, proxy2, start, finish, alloc, size = value.split("pppppi")
else:
start, finish, alloc, size = value.split("pppi")
offset += 2 * d.ptrSize() # _Myproxy and _MyVal2._Myproxy
core = d.createValue(value.address() + offset, value.type)
first, last, end, size = core.split("pppi")
d.check(0 <= size and size <= 1000 * 1000 * 1000)
qdumpHelper__std__vector__bool(d, start, size, inner_type)
qdumpHelper__std__vector__bool(d, first, size, inner_type)
else:
if d.isDebugBuild is None:
try:
@@ -1096,13 +1144,17 @@ def qdumpHelper__std__vector__msvc(d, value):
d.isDebugBuild = True
except RuntimeError:
d.isDebugBuild = False
alloc_size = value.type[1].size() # see disclaimer #1
# see disclaimer #2
offset = alloc_size if alloc_size > 1 else 0
if d.isDebugBuild:
proxy, start, finish, alloc = value.split("pppp")
else:
start, finish, alloc = value.split("ppp")
size = (finish - start) // inner_type.size()
offset += d.ptrSize() # _MyVal2._Myproxy
core = d.createValue(value.address() + offset, value.type)
first, last, end = core.split(f'ppp')
size = (last - first) // inner_type.size()
d.check(0 <= size and size <= 1000 * 1000 * 1000)
qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type)
qdumpHelper__std__vector__nonbool(d, first, last, end, inner_type)
def qform__std____debug__vector():

View File

@@ -87,6 +87,199 @@ static inline QString fixNestedTemplates(QString s)
return s;
}
static void simplifyAllocator(
const QString &typeIn,
const QString &allocatorTemplateHead,
const QString &containerTypePrefix,
const bool isLibCpp,
QString &type)
{
const int allocatorTemplateHeadLength = allocatorTemplateHead.length();
int start = type.indexOf(allocatorTemplateHead);
if (start != -1) {
// search for matching '>'
int pos;
int level = 0;
for (pos = start + allocatorTemplateHeadLength - 1; pos < type.size(); ++pos) {
int c = type.at(pos).unicode();
if (c == '<') {
++level;
} else if (c == '>') {
--level;
if (level == 0)
break;
}
}
const QString alloc = fixNestedTemplates(type.mid(start, pos + 1 - start).trimmed());
const QString inner = fixNestedTemplates(
alloc.mid(allocatorTemplateHeadLength, alloc.size() - allocatorTemplateHeadLength - 1)
.trimmed());
const QString allocEsc = QRegularExpression::escape(alloc);
const QString innerEsc = QRegularExpression::escape(inner);
if (inner == "char") { // std::string
simplifyStdString("char", "string", &type);
} else if (inner == "wchar_t") { // std::wstring
simplifyStdString("wchar_t", "wstring", &type);
} else if (inner == "unsigned short") { // std::wstring/MSVC
simplifyStdString("unsigned short", "wstring", &type);
}
// std::vector, std::deque, std::list
QRegularExpression re1(QString::fromLatin1("(vector|(forward_)?list|deque)<%1, ?%2\\s*>")
.arg(innerEsc, allocEsc));
QTC_ASSERT(re1.isValid(), return);
QRegularExpressionMatch match = re1.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1%2<%3>").arg(containerTypePrefix, match.captured(1), inner));
// std::stack
QRegularExpression stackRE(
QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(innerEsc, innerEsc));
QTC_ASSERT(stackRE.isValid(), return);
match = stackRE.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1stack<%2>").arg(containerTypePrefix, inner));
// std::hash<char>
QRegularExpression hashCharRE(
QString::fromLatin1("hash<char, std::char_traits<char>, ?%1\\s*?>").arg(allocEsc));
QTC_ASSERT(hashCharRE.isValid(), return);
match = hashCharRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("hash<char>"));
// std::set
QRegularExpression setRE(QString::fromLatin1("(multi)?set<%1, ?std::less<%2>, ?%3\\s*?>")
.arg(innerEsc, innerEsc, allocEsc));
QTC_ASSERT(setRE.isValid(), return);
match = setRE.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1%2set<%3>")
.arg(containerTypePrefix, match.captured(1), inner));
// std::unordered_set
QRegularExpression unorderedSetRE(
QString::fromLatin1(
"unordered_(multi)?set<%1, ?std::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
QTC_ASSERT(unorderedSetRE.isValid(), return);
match = unorderedSetRE.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1unordered_%2set<%3>")
.arg(containerTypePrefix, match.captured(1), inner));
// boost::unordered_set
QRegularExpression boostUnorderedSetRE(
QString::fromLatin1("unordered_set<%1, ?boost::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
QTC_ASSERT(boostUnorderedSetRE.isValid(), return);
match = boostUnorderedSetRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("unordered_set<%1>").arg(inner));
// std::map
if (inner.startsWith("std::pair<")) {
// search for outermost ',', split key and value
int pos;
int level = 0;
for (pos = 10; pos < inner.size(); ++pos) {
int c = inner.at(pos).unicode();
if (c == '<')
++level;
else if (c == '>')
--level;
else if (c == ',' && level == 0)
break;
}
const QString key = chopConst(inner.mid(10, pos - 10));
const QString keyEsc = QRegularExpression::escape(key);
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
if (inner.at(++pos) == ' ')
pos++;
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
const QString valueEsc = QRegularExpression::escape(value);
QRegularExpression mapRE1(
QString::fromLatin1("(multi)?map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
QTC_ASSERT(mapRE1.isValid(), return);
match = mapRE1.match(type);
if (match.hasMatch()) {
type.replace(
match.captured(),
QString::fromLatin1("%1%2map<%3, %4>")
.arg(containerTypePrefix, match.captured(1), key, value));
} else {
QRegularExpression mapRE2(
QString::fromLatin1(
"(multi)?map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
match = mapRE2.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1%2map<const %3, %4>")
.arg(containerTypePrefix, match.captured(1), key, value));
}
}
// std::unordered_map
if (inner.startsWith("std::pair<")) {
// search for outermost ',', split key and value
int pos;
int level = 0;
for (pos = 10; pos < inner.size(); ++pos) {
int c = inner.at(pos).unicode();
if (c == '<')
++level;
else if (c == '>')
--level;
else if (c == ',' && level == 0)
break;
}
const QString key = chopConst(inner.mid(10, pos - 10));
const QString keyEsc = QRegularExpression::escape(key);
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
if (inner.at(++pos) == ' ')
pos++;
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
const QString valueEsc = QRegularExpression::escape(value);
QRegularExpression mapRE1(
QString::fromLatin1("unordered_(multi)?map<%1, ?%2, ?std::hash<%3 ?>, "
"?std::equal_to<%4 ?>, ?%5\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, keyEsc, allocEsc));
QTC_ASSERT(mapRE1.isValid(), return);
match = mapRE1.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1unordered_%2map<%3, %4>")
.arg(containerTypePrefix, match.captured(1), key, value));
if (isLibCpp) {
QRegularExpression mapRE2(
QString::fromLatin1("unordered_(multi)?map<std::string, ?%1, "
"?std::hash<char>, ?std::equal_to<std::string>, ?%2\\s*?>")
.arg(valueEsc, allocEsc));
QTC_ASSERT(mapRE2.isValid(), return);
match = mapRE2.match(type);
if (match.hasMatch())
type.replace(
match.captured(),
QString::fromLatin1("%1unordered_%2map<std::string, %3>")
.arg(containerTypePrefix, match.captured(1), value));
}
}
}
}
QString simplifyType(const QString &typeIn)
{
QString type = typeIn;
@@ -140,153 +333,10 @@ QString simplifyType(const QString &typeIn)
type.replace(match.captured(), match.captured(1));
}
// Anything with a std::allocator
int start = type.indexOf("std::allocator<");
if (start != -1) {
// search for matching '>'
int pos;
int level = 0;
for (pos = start + 12; pos < type.size(); ++pos) {
int c = type.at(pos).unicode();
if (c == '<') {
++level;
} else if (c == '>') {
--level;
if (level == 0)
break;
}
}
const QString alloc = fixNestedTemplates(type.mid(start, pos + 1 - start).trimmed());
const QString inner = fixNestedTemplates(alloc.mid(15, alloc.size() - 16).trimmed());
const QString allocEsc = QRegularExpression::escape(alloc);
const QString innerEsc = QRegularExpression::escape(inner);
if (inner == "char") { // std::string
simplifyStdString("char", "string", &type);
} else if (inner == "wchar_t") { // std::wstring
simplifyStdString("wchar_t", "wstring", &type);
} else if (inner == "unsigned short") { // std::wstring/MSVC
simplifyStdString("unsigned short", "wstring", &type);
}
// std::vector, std::deque, std::list
QRegularExpression re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(innerEsc, allocEsc));
QTC_ASSERT(re1.isValid(), return typeIn);
QRegularExpressionMatch match = re1.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("%1<%2>").arg(match.captured(1), inner));
// std::stack
QRegularExpression stackRE(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(innerEsc, innerEsc));
QTC_ASSERT(stackRE.isValid(), return typeIn);
match = stackRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("stack<%1>").arg(inner));
// std::hash<char>
QRegularExpression hashCharRE(QString::fromLatin1("hash<char, std::char_traits<char>, ?%1\\s*?>").arg(allocEsc));
QTC_ASSERT(hashCharRE.isValid(), return typeIn);
match = hashCharRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("hash<char>"));
// std::set
QRegularExpression setRE(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*?>").arg(innerEsc, innerEsc, allocEsc));
QTC_ASSERT(setRE.isValid(), return typeIn);
match = setRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("set<%1>").arg(inner));
// std::unordered_set
QRegularExpression unorderedSetRE(QString::fromLatin1("unordered_(multi)?set<%1, ?std::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
QTC_ASSERT(unorderedSetRE.isValid(), return typeIn);
match = unorderedSetRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("unordered_%1set<%2>").arg(match.captured(1), inner));
// boost::unordered_set
QRegularExpression boostUnorderedSetRE(QString::fromLatin1("unordered_set<%1, ?boost::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
QTC_ASSERT(boostUnorderedSetRE.isValid(), return typeIn);
match = boostUnorderedSetRE.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("unordered_set<%1>").arg(inner));
// std::map
if (inner.startsWith("std::pair<")) {
// search for outermost ',', split key and value
int pos;
int level = 0;
for (pos = 10; pos < inner.size(); ++pos) {
int c = inner.at(pos).unicode();
if (c == '<')
++level;
else if (c == '>')
--level;
else if (c == ',' && level == 0)
break;
}
const QString key = chopConst(inner.mid(10, pos - 10));
const QString keyEsc = QRegularExpression::escape(key);
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
if (inner.at(++pos) == ' ')
pos++;
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
const QString valueEsc = QRegularExpression::escape(value);
QRegularExpression mapRE1(QString::fromLatin1("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
QTC_ASSERT(mapRE1.isValid(), return typeIn);
match = mapRE1.match(type);
if (match.hasMatch()) {
type.replace(match.captured(), QString::fromLatin1("map<%1, %2>").arg(key, value));
} else {
QRegularExpression mapRE2(QString::fromLatin1("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
match = mapRE2.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("map<const %1, %2>").arg(key, value));
}
}
// std::unordered_map
if (inner.startsWith("std::pair<")) {
// search for outermost ',', split key and value
int pos;
int level = 0;
for (pos = 10; pos < inner.size(); ++pos) {
int c = inner.at(pos).unicode();
if (c == '<')
++level;
else if (c == '>')
--level;
else if (c == ',' && level == 0)
break;
}
const QString key = chopConst(inner.mid(10, pos - 10));
const QString keyEsc = QRegularExpression::escape(key);
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
if (inner.at(++pos) == ' ')
pos++;
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
const QString valueEsc = QRegularExpression::escape(value);
QRegularExpression mapRE1(QString::fromLatin1("unordered_(multi)?map<%1, ?%2, ?std::hash<%3 ?>, ?std::equal_to<%4 ?>, ?%5\\s*?>")
.arg(keyEsc, valueEsc, keyEsc, keyEsc, allocEsc));
QTC_ASSERT(mapRE1.isValid(), return typeIn);
match = mapRE1.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("unordered_%1map<%2, %3>").arg(match.captured(1), key, value));
if (isLibCpp) {
QRegularExpression mapRE2(QString::fromLatin1("unordered_map<std::string, ?%1, "
"?std::hash<char>, ?std::equal_to<std::string>, ?%2\\s*?>")
.arg(valueEsc, allocEsc));
QTC_ASSERT(mapRE2.isValid(), return typeIn);
match = mapRE2.match(type);
if (match.hasMatch())
type.replace(match.captured(), QString::fromLatin1("unordered_map<std::string, %2>").arg(value));
}
}
} // with std::allocator
// Fix e.g. `std::vector<T, std::allocator<T>> -> std::vector<T>`
simplifyAllocator(typeIn, "std::allocator<", "", isLibCpp, type);
// Fix e.g. `std::vector<T, std::pmr::polynorphic_allocator<T>> -> std::pmr::vector<T>`
simplifyAllocator(typeIn, "std::pmr::polymorphic_allocator<", "pmr::", isLibCpp, type);
}
type.replace('@', " *");
type.replace(" >", ">");

View File

@@ -4838,7 +4838,7 @@ void tst_Dumpers::dumper_data()
"};\n\n"
"std::deque<MyItem> deq;\n"
"for (int i = 0; i < 100; ++i)\n"
"for (uint16_t i = 0; i < 100; ++i)\n"
" deq.push_back({i, i});\n",
"&deq")
@@ -8684,6 +8684,96 @@ void tst_Dumpers::dumper_data()
+ Check{"err_class", "Unexpected", "tl::expected<ErrCode, MyClass>"}
+ Check{"err_class.inner.m_x", "10", "int"};
// clang-format on
// clang-format off
QTest::newRow("QTCREATORBUG-30224-StdLibPmrContainers") << Data{
R"(
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
)",
R"(
std::pmr::deque<int> d;
std::pmr::forward_list<int> fl;
std::pmr::list<int> l;
std::pmr::set<int> s;
std::pmr::multiset<int> ms;
std::pmr::unordered_set<int> us;
std::pmr::unordered_multiset<int> ums;
std::pmr::map<int, int> m;
std::pmr::multimap<int, int> mm;
std::pmr::unordered_map<int, int> um;
std::pmr::unordered_multimap<int, int> umm;
std::pmr::vector<int> v;
constexpr auto count = 10;
for (auto i = 0; i < count; ++i)
{
if (i < count / 2) {
d.push_back(i);
}
else {
d.push_front(i);
}
fl.push_front(i);
l.push_back(i);
s.insert(i);
ms.insert(i);
us.insert(i);
ums.insert(i);
m.emplace(i, i);
mm.emplace(i, i);
um.emplace(i, i);
umm.emplace(i, i);
v.push_back(i);
}
)",
"&d, &fl, &l, &s, &ms, &us, &ums, &m, &mm, &um, &umm, &v"
}
+ Check{"d", "<10 items>", "std::pmr::deque<int>"} % NoGdbEngine
+ Check{"d", "<10 items>", "std::pmr::deque"} % GdbEngine
+ Check{"d.1", "[1]", "8", "int"}
+ Check{"fl", "<10 items>", "std::pmr::forward_list<int>"} % NoGdbEngine
+ Check{"fl", "<10 items>", "std::pmr::forward_list"} % GdbEngine
+ Check{"fl.2", "[2]", "7", "int"}
+ Check{"l", "<10 items>", "std::pmr::list<int>"} % NoGdbEngine
+ Check{"l", "<10 items>", "std::pmr::list"} % GdbEngine
+ Check{"l.3", "[3]", "3", "int"}
+ Check{"s", "<10 items>", "std::pmr::set<int>"} % NoGdbEngine
+ Check{"s", "<10 items>", "std::pmr::set"} % GdbEngine
+ Check{"s.4", "[4]", "4", "int"}
+ Check{"ms", "<10 items>", "std::pmr::multiset<int>"} % NoGdbEngine
+ Check{"ms", "<10 items>", "std::pmr::multiset"} % GdbEngine
+ Check{"ms.4", "[4]", "4", "int"}
+ Check{"us", "<10 items>", "std::pmr::unordered_set<int>"} % NoGdbEngine
+ Check{"us", "<10 items>", "std::pmr::unordered_set"} % GdbEngine
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset<int>"} % NoGdbEngine
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset"} % GdbEngine
+ Check{"m", "<10 items>", "std::pmr::map<int, int>"} % NoGdbEngine
+ Check{"m", "<10 items>", "std::pmr::map"} % GdbEngine
+ Check{"m.5", "[5] 5", "5", ""}
+ Check{"mm", "<10 items>", "std::pmr::multimap<int, int>"} % NoGdbEngine
+ Check{"mm", "<10 items>", "std::pmr::multimap"} % GdbEngine
+ Check{"mm.5", "[5] 5", "5", ""}
+ Check{"um", "<10 items>", "std::pmr::unordered_map<int, int>"} % NoGdbEngine
+ Check{"um", "<10 items>", "std::pmr::unordered_map"} % GdbEngine
+ Check{"umm", "<10 items>", "std::pmr::unordered_multimap<int, int>"} % NoGdbEngine
+ Check{"umm", "<10 items>", "std::pmr::unordered_multimap"} % GdbEngine
+ Check{"v", "<10 items>", "std::pmr::vector<int>"} % NoGdbEngine
+ Check{"v", "<10 items>", "std::pmr::vector"} % GdbEngine
+ Check{"v.6", "[6]", "6", "int"};
// clang-format on
}
int main(int argc, char *argv[])