facebookexperimental / object-introspection

Object Introspection (OI) enables on-demand, hierarchical profiling of objects in arbitrary C/C++ programs with no recompilation.
Apache License 2.0
160 stars 13 forks source link

tbv2: delegate is required for complex processors when write should work #466

Open JakeHillion opened 7 months ago

JakeHillion commented 7 months ago

When writing container implementations for some complex types it makes sense to extract multiple component numbers from the data buffer in one go. For example it would be natural to write processors like this:

[codegen.processor]
type = "types::st::Pair<DB, types::st::VarInt<DB>, types::st::Pair<DB, types::st::VarInt<DB>, types::st::VarInt<DB>>>"
func = "..."
[codegen.processor]
type = "types::st::List<DB, types::st::VarInt<DB>>"
func = "..."

This generally works and should be a supported pattern.

traversal_func = '''
auto tail = returnArg.write(a)
    .write(b)
    .write(c)
    .write(listLength);
for (auto&& it : container) {
  tail = tail.delegate([&ctx, &it](auto ret) {
    return OIInternal::getSizeType<Ctx>(ctx, it, ret);
  });
}
return tail.finish();
'''

However, this doesn't work. The chained writes are invalid because it's attempting to write to a Pair<Pair<VarInt, ...>, ...>.

The generated type is (DB omitted for clarity): using type = Pair<Pair<VarInt, Pair<VarInt, VarInt>>, List<VarInt>>

At least for this layer this is really easy to fix. Instead of guaranteeing the first pair returns a Unit from the write, see what it returns and instead return a Pair<decltype(first.write(X)), second>. I think this scales too.