object-introspection/types/std_map_type.toml
Jake Hillion 4c047b5f91 tbv2: add is_primitive to output
C++ has a concept of Primitive which holds in the type graph. However we don't
currently expose this information to the end user. Expose this from the OIL
iterator to allow future features like primitive rollups.

This affects containers like maps which have a fake `[]` element with no type.
They use this to group together the key/value in a map and to account for any
per element storage overhead. Currently the decision is to make the fake `[]`
element a primitive if all of its children are primitives. This allows for more
effective primitive rollups if that is implemented. This implementation detail
may be changed in future.

Test Plan:
- CI
- Updated simple tests.
2024-01-16 11:14:13 +00:00

150 lines
4.7 KiB
TOML

[info]
type_name = "std::map"
stub_template_params = [2,3]
ctype = "STD_MAP_TYPE"
header = "map"
# Old:
typeName = "std::map<"
ns = ["namespace std"]
numTemplateParams = 2
replaceTemplateParamIndex = [2]
allocatorIndex = 3
[codegen]
decl = """
template<class K, class T, class C, class A>
void getSizeType(const %1%<K, T, C, A> &container, size_t& returnArg);
"""
func = """
template<class K, class T, class C, class A>
void getSizeType(const %1%<K, T, C, A> &container, size_t& returnArg)
{
constexpr size_t nodeSize = sizeof(typename %1%<K, T, C, A>::node_type);
size_t numElems = container.size();
SAVE_SIZE(sizeof(%1%<K, T, C, A>) + (nodeSize * numElems));
SAVE_DATA((uintptr_t)nodeSize);
SAVE_DATA((uintptr_t)numElems);
for (auto const& it : container)
{
getSizeType(it.first, returnArg);
getSizeType(it.second, returnArg);
}
}
"""
traversal_func = """
auto tail = returnArg
.write((uintptr_t)&container)
.write(container.size());
for (const auto &entry: container) {
tail = tail.delegate([&ctx, &key = entry.first, &value = entry.second](auto ret) {
auto start = maybeCaptureKey<captureKeys, Ctx, T0>(ctx, key, ret);
auto next = start.delegate([&ctx, &key](typename TypeHandler<Ctx, T0>::type ret) {
return OIInternal::getSizeType<Ctx>(ctx, key, ret);
});
return OIInternal::getSizeType<Ctx>(ctx, value, next);
});
}
return tail.finish();
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = "el.pointer = std::get<ParsedData::VarInt>(d.val).value;"
[[codegen.processor]]
type = """
std::conditional_t<captureKeys,
types::st::List<DB,
types::st::Pair<DB,
typename CaptureKeyHandler<Ctx, T0>::type,
types::st::Pair<DB,
typename TypeHandler<Ctx, T0>::type,
typename TypeHandler<Ctx, T1>::type>>>,
types::st::List<DB,
types::st::Pair<DB,
typename TypeHandler<Ctx, T0>::type,
typename TypeHandler<Ctx, T1>::type>>
>
"""
func = """
#ifdef __GLIBCXX__
/* We don't have access to the _Rb_tree_node struct, so we manually re-create it
* to get the effective size of a map entry. Is there a better way to do this?
*
* https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a00716_source.html#l00216
* From the source of <bits/stl_tree.h>, an _Rb_tree_node has the following members:
*/
struct OI_Rb_tree_node {
using _Rb_tree_color = int; // enum
using _Base_ptr = std::nullptr_t; // pointer
using _value_type = std::pair<const T0, T1>; // from https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a00692_source.html#l00105
_Rb_tree_color _M_color; // from parent _Rb_tree_node_base
_Base_ptr _M_parent; // from parent _Rb_tree_node_base
_Base_ptr _M_left; // from parent _Rb_tree_node_base
_Base_ptr _M_right; // from parent _Rb_tree_node_base
_value_type _M_value;
};
static constexpr size_t element_size = sizeof(OI_Rb_tree_node);
#elif _LIBCPP_VERSION
static_assert(false && "libc++ is currently not supported");
/* We don't have access to the __tree_node struct, so we manually re-create it
* to get the effective size of a map entry. Is there a better way to do this?
*
* https://github.com/llvm/llvm-project/blob/1b10920164695a487669405223f8bbe93799430c/libcxx/include/__tree#L729-L781
* From the source of <__tree>, a __tree_node has the following members:
*/
struct OI__tree_node {
using pointer = std::nullptr_t; // pointer
using __parent_pointer = std::nullptr_t; // pointer
using _value_pair = std::pair<const T0, T1>; // from https://github.com/llvm/llvm-project/blob/main/libcxx/include/map#L1024
pointer __left_; // from parent __tree_end_node
pointer __right_; // from parent __tree_node_base
__parent_pointer __parent_; // from parent __tree_node_base
bool __is_black_; // from parent __tree_node_base
_value_type __value_;
};
static constexpr size_t element_size = sizeof(OI__tree_node);
#else
static_assert(false && "No known element_size for sets. See types/std_map_type.toml");
#endif
static constexpr std::array<inst::Field, 2> element_fields{
make_field<Ctx, T0>("key"),
make_field<Ctx, T1>("value"),
};
static constexpr auto processors = maybeCaptureKeysProcessor<captureKeys, Ctx, T0>();
static constexpr inst::Field element{
element_size,
element_size - sizeof(T0) - sizeof(T1),
"[]",
std::array<std::string_view, 0>{},
element_fields,
processors,
element_fields[0].is_primitive && element_fields[1].is_primitive,
};
auto list = std::get<ParsedData::List>(d.val);
el.container_stats.emplace(result::Element::ContainerStats {
.capacity = list.length,
.length = list.length,
});
for (size_t i = 0; i < list.length; i++)
stack_ins(element);
"""