Project
Loading...
Searching...
No Matches
AnalysisManagers.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#ifndef FRAMEWORK_ANALYSISMANAGERS_H
13#define FRAMEWORK_ANALYSISMANAGERS_H
14#include "DataAllocator.h"
18#include "Framework/ASoA.h"
26#include "Framework/Condition.h"
31
32namespace o2::framework
33{
34
35namespace
36{
37template <typename O>
38static inline auto extractOriginal(ProcessingContext& pc)
39{
40 return pc.inputs().get<TableConsumer>(aod::MetadataTrait<O>::metadata::tableLabel())->asArrowTable();
41}
42
43template <typename... Os>
44static inline std::vector<std::shared_ptr<arrow::Table>> extractOriginals(framework::pack<Os...>, ProcessingContext& pc)
45{
46 return {extractOriginal<Os>(pc)...};
47}
48
49template <size_t N, std::array<soa::TableRef, N> refs>
50static inline auto extractOriginals(ProcessingContext& pc)
51{
52 return [&]<size_t... Is>(std::index_sequence<Is...>) -> std::vector<std::shared_ptr<arrow::Table>> {
53 return {pc.inputs().get<TableConsumer>(o2::aod::label<refs[Is]>())->asArrowTable()...};
54 }(std::make_index_sequence<refs.size()>());
55}
56} // namespace
57
58namespace analysis_task_parsers
59{
60
62template <typename O>
63bool appendOption(std::vector<ConfigParamSpec>&, O&)
64{
65 return false;
66}
67
68template <is_configurable O>
69bool appendOption(std::vector<ConfigParamSpec>& options, O& option)
70{
71 return ConfigurableHelpers::appendOption(options, option);
72}
73
74template <is_configurable_group O>
75bool appendOption(std::vector<ConfigParamSpec>& options, O& optionGroup)
76{
77 if constexpr (requires { optionGroup.prefix; }) {
78 homogeneous_apply_refs<true>([prefix = optionGroup.prefix]<typename C>(C& option) { // apend group prefix if set
79 if constexpr (requires { option.name; }) {
80 option.name.insert(0, 1, '.');
81 option.name.insert(0, prefix);
82 }
83 return true;
84 },
85 optionGroup);
86 }
87 homogeneous_apply_refs<true>([&options](auto& option) { return appendOption(options, option); }, optionGroup);
88 return true;
89}
90
91template <typename O>
93{
94 return false;
95}
96
97template <is_configurable O>
98bool prepareOption(InitContext& context, O& configurable)
99{
100 if constexpr (variant_trait_v<typename O::type> != VariantType::Unknown) {
101 configurable.value = context.options().get<typename O::type>(configurable.name.c_str());
102 } else {
103 auto pt = context.options().get<boost::property_tree::ptree>(configurable.name.c_str());
104 configurable.value = RootConfigParamHelpers::as<typename O::type>(pt);
105 }
106 return true;
107}
108
109template <is_configurable_group O>
110bool prepareOption(InitContext& context, O& configurableGroup)
111{
112 homogeneous_apply_refs<true>([&context](auto&& configurable) { return prepareOption(context, configurable); }, configurableGroup);
113 return true;
114}
115
117template <typename C>
118bool appendCondition(std::vector<InputSpec>&, C&)
119{
120 return false;
121}
122
123template <is_condition C>
124bool appendCondition(std::vector<InputSpec>& inputs, C& condition)
125{
126 inputs.emplace_back(InputSpec{condition.path, "AODC", runtime_hash(condition.path.c_str()), Lifetime::Condition, ccdbParamSpec(condition.path)});
127 return true;
128}
129
130template <is_condition_group C>
131bool appendCondition(std::vector<InputSpec>& inputs, C& conditionGroup)
132{
133 homogeneous_apply_refs<true>([&inputs](auto& condition) { return appendCondition(inputs, condition); }, conditionGroup);
134 return true;
135}
136
138template <typename T>
139bool requestInputs(std::vector<InputSpec>&, T const&)
140{
141 return false;
142}
143
144template <is_spawns T>
145const char* controlOption()
146{
147 return "control:spawn";
148}
149
150template <is_builds T>
151const char* controlOption()
152{
153 return "control:build";
154}
155
156template <is_defines T>
157const char* controlOption()
158{
159 return "control:define";
160}
161
162template <typename T>
163concept with_base_table = requires(T const& t) { t.base_specs(); };
164
165template <with_base_table T>
166bool requestInputs(std::vector<InputSpec>& inputs, T const& entity)
167{
168 auto base_specs = entity.base_specs();
169 for (auto base_spec : base_specs) {
170 base_spec.metadata.push_back(ConfigParamSpec{std::string{controlOption<T>()}, VariantType::Bool, true, {"\"\""}});
171 DataSpecUtils::updateInputList(inputs, std::forward<InputSpec>(base_spec));
172 }
173 return true;
174}
175
176template <typename C>
178{
179 return false;
180}
181
182template <is_condition C>
183bool newDataframeCondition(InputRecord& record, C& condition)
184{
185 condition.instance = (typename C::type*)record.get<typename C::type*>(condition.path).get();
186 return true;
187}
188
189template <is_condition_group C>
190bool newDataframeCondition(InputRecord& record, C& conditionGroup)
191{
192 homogeneous_apply_refs<true>([&record](auto&& condition) { return newDataframeCondition(record, condition); }, conditionGroup);
193 return true;
194}
195
197template <typename T>
198bool appendOutput(std::vector<OutputSpec>&, T&, uint32_t)
199{
200 return false;
201}
202
203template <is_produces T>
204bool appendOutput(std::vector<OutputSpec>& outputs, T&, uint32_t)
205{
207 return true;
208}
209
210template <is_produces_group T>
211bool appendOutput(std::vector<OutputSpec>& outputs, T& producesGroup, uint32_t hash)
212{
213 homogeneous_apply_refs<true>([&outputs, hash](auto& produces) { return appendOutput(outputs, produces, hash); }, producesGroup);
214 return true;
215}
216
217template <is_histogram_registry T>
218bool appendOutput(std::vector<OutputSpec>& outputs, T& hr, uint32_t hash)
219{
220 hr.setHash(hash);
221 outputs.emplace_back(hr.spec());
222 return true;
223}
224
225template <is_outputobj T>
226bool appendOutput(std::vector<OutputSpec>& outputs, T& obj, uint32_t hash)
227{
228 obj.setHash(hash);
229 outputs.emplace_back(obj.spec());
230 return true;
231}
232
233template <typename T>
234 requires(is_spawns<T> || is_builds<T> || is_defines<T>)
235bool appendOutput(std::vector<OutputSpec>& outputs, T& entity, uint32_t)
236{
237 outputs.emplace_back(entity.spec());
238 return true;
239}
240
241template <typename T>
243{
244 return false;
245}
246
247template <is_histogram_registry T>
248bool postRunOutput(EndOfStreamContext& context, T& hr)
249{
250 auto& deviceSpec = context.services().get<o2::framework::DeviceSpec const>();
251 auto sendHistos = [deviceSpec, &context](HistogramRegistry const& self, TNamed* obj) mutable {
252 context.outputs().snapshot(self.ref(deviceSpec.inputTimesliceId, deviceSpec.maxInputTimeslices), *obj);
253 };
254 hr.apply(sendHistos);
255 hr.clean();
256 return true;
257}
258
259template <is_outputobj T>
260bool postRunOutput(EndOfStreamContext& context, T& obj)
261{
262 auto& deviceSpec = context.services().get<o2::framework::DeviceSpec const>();
263 context.outputs().snapshot(obj.ref(deviceSpec.inputTimesliceId, deviceSpec.maxInputTimeslices), *obj);
264 return true;
265}
266
267template <typename T>
269{
270 return false;
271}
272
273template <is_produces T>
274bool prepareOutput(ProcessingContext& context, T& produces)
275{
276 produces.resetCursor(std::move(context.outputs().make<TableBuilder>(OutputForTable<typename T::persistent_table_t>::ref())));
277 return true;
278}
279
280template <is_produces_group T>
281bool prepareOutput(ProcessingContext& context, T& producesGroup)
282{
283 homogeneous_apply_refs<true>([&context](auto& produces) { return prepareOutput(context, produces); }, producesGroup);
284 return true;
285}
286
287template <is_spawns T>
288bool prepareOutput(ProcessingContext& context, T& spawns)
289{
290 using metadata = o2::aod::MetadataTrait<o2::aod::Hash<T::spawnable_t::ref.desc_hash>>::metadata;
291 auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals<metadata::sources.size(), metadata::sources>(context), std::span{metadata::base_table_t::originalLabels});
292 if (originalTable->schema()->fields().empty() == true) {
293 using base_table_t = typename T::base_table_t::table_t;
294 originalTable = makeEmptyTable<base_table_t>(o2::aod::label<metadata::extension_table_t::ref>());
295 }
296 using D = o2::aod::Hash<metadata::extension_table_t::ref.desc_hash>;
297
298 spawns.extension = std::make_shared<typename T::extension_t>(o2::framework::spawner<D>(originalTable,
299 o2::aod::label<metadata::extension_table_t::ref>(),
300 spawns.projectors.data(),
301 spawns.projector,
302 spawns.schema));
303 spawns.table = std::make_shared<typename T::spawnable_t::table_t>(soa::ArrowHelpers::joinTables({spawns.extension->asArrowTable(), originalTable}, std::span{T::spawnable_t::table_t::originalLabels}));
304 return true;
305}
306
307template <is_builds T>
308bool prepareOutput(ProcessingContext& context, T& builds)
309{
310 using metadata = o2::aod::MetadataTrait<o2::aod::Hash<T::buildable_t::ref.desc_hash>>::metadata;
311 return builds.template build<typename T::buildable_t::indexing_t>(builds.pack(), extractOriginals<metadata::sources.size(), metadata::sources>(context));
312}
313
314template <is_defines T>
315bool prepareOutput(ProcessingContext& context, T& defines)
316 requires(T::delayed == false)
317{
318 using metadata = o2::aod::MetadataTrait<o2::aod::Hash<T::spawnable_t::ref.desc_hash>>::metadata;
319 auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals<metadata::sources.size(), metadata::sources>(context), std::span{metadata::base_table_t::originalLabels});
320 if (originalTable->schema()->fields().empty() == true) {
321 using base_table_t = typename T::base_table_t::table_t;
322 originalTable = makeEmptyTable<base_table_t>(o2::aod::label<metadata::extension_table_t::ref>());
323 }
324 if (defines.inputSchema == nullptr) {
325 defines.inputSchema = originalTable->schema();
326 }
327 using D = o2::aod::Hash<metadata::extension_table_t::ref.desc_hash>;
328
329 defines.extension = std::make_shared<typename T::extension_t>(o2::framework::spawner<D>(originalTable,
330 o2::aod::label<metadata::extension_table_t::ref>(),
331 defines.projectors.data(),
332 defines.projector,
333 defines.schema));
334 defines.table = std::make_shared<typename T::spawnable_t::table_t>(soa::ArrowHelpers::joinTables({defines.extension->asArrowTable(), originalTable}, std::span{T::spawnable_t::table_t::originalLabels}));
335 return true;
336}
337
338template <typename T>
340{
341 return false;
342}
343
344template <is_defines T>
345 requires(T::delayed == true)
346bool prepareDelayedOutput(ProcessingContext& context, T& defines)
347{
348 if (defines.needRecompilation) {
349 defines.recompile();
350 }
351 using metadata = o2::aod::MetadataTrait<o2::aod::Hash<T::spawnable_t::ref.desc_hash>>::metadata;
352 auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals<metadata::sources.size(), metadata::sources>(context), std::span{metadata::base_table_t::originalLabels});
353 if (originalTable->schema()->fields().empty() == true) {
354 using base_table_t = typename T::base_table_t::table_t;
355 originalTable = makeEmptyTable<base_table_t>(o2::aod::label<metadata::extension_table_t::ref>());
356 }
357 if (defines.inputSchema == nullptr) {
358 defines.inputSchema = originalTable->schema();
359 }
360 using D = o2::aod::Hash<metadata::extension_table_t::ref.desc_hash>;
361
362 defines.extension = std::make_shared<typename T::extension_t>(o2::framework::spawner<D>(originalTable,
363 o2::aod::label<metadata::extension_table_t::ref>(),
364 defines.projectors.data(),
365 defines.projector,
366 defines.schema));
367 defines.table = std::make_shared<typename T::spawnable_t::table_t>(soa::ArrowHelpers::joinTables({defines.extension->asArrowTable(), originalTable}, std::span{T::spawnable_t::table_t::originalLabels}));
368 return true;
369}
370
371template <typename T>
373{
374 return false;
375}
376
377template <is_produces T>
378bool finalizeOutput(ProcessingContext&, T& produces)
379{
380 produces.setLabel(o2::aod::label<T::persistent_table_t::ref>());
381 produces.release();
382 return true;
383}
384
385template <is_produces_group T>
386bool finalizeOutput(ProcessingContext& context, T& producesGroup)
387{
388 homogeneous_apply_refs<true>([&context](auto& produces) { return finalizeOutput(context, produces); }, producesGroup);
389 return true;
390}
391
392template <is_spawns T>
393bool finalizeOutput(ProcessingContext& context, T& spawns)
394{
395 context.outputs().adopt(spawns.output(), spawns.asArrowTable());
396 return true;
397}
398
399template <is_builds T>
400bool finalizeOutput(ProcessingContext& context, T& builds)
401{
402 context.outputs().adopt(builds.output(), builds.asArrowTable());
403 return true;
404}
405
406template <is_defines T>
407bool finalizeOutput(ProcessingContext& context, T& defines)
408{
409 context.outputs().adopt(defines.output(), defines.asArrowTable());
410 return true;
411}
412
414template <typename T>
415bool addService(std::vector<ServiceSpec>&, T&)
416{
417 return false;
418}
419
420template <is_service T>
421bool addService(std::vector<ServiceSpec>& specs, T&)
422{
424 auto p = typename T::service_t{};
425 auto loadableServices = PluginManager::parsePluginSpecString(p.loadSpec.c_str());
426 PluginManager::loadFromPlugin<ServiceSpec, ServicePlugin>(loadableServices, specs);
427 }
428 return true;
429}
430
431template <typename T>
433{
434 return false;
435}
436
437template <is_service T>
438bool prepareService(InitContext& context, T& service)
439{
440 using S = typename T::service_t;
441 if constexpr (requires { &S::instance; }) {
442 service.service = &(S::instance()); // Sigh...
443 return true;
444 } else {
445 service.service = &(context.services().get<S>());
446 return true;
447 }
448 return false;
449}
450
451template <typename T>
453{
454 return false;
455}
456
457template <is_service T>
458bool postRunService(EndOfStreamContext&, T& service)
459{
460 // FIXME: for the moment we only need endOfStream to be
461 // stateless. In the future we might want to pass it EndOfStreamContext
462 if constexpr (requires { &T::service_t::endOfStream; }) {
463 service.service->endOfStream();
464 return true;
465 }
466 return false;
467}
468
470template <typename T>
472{
473 return false;
474}
475
476template <expressions::is_filter T>
477bool updatePlaceholders(InitContext& context, T& filter)
478{
480 return true;
481}
482
483template <is_partition T>
484bool updatePlaceholders(InitContext& context, T& partition)
485{
486 partition.updatePlaceholders(context);
487 return true;
488}
489
490template <typename T>
491bool createExpressionTrees(std::vector<ExpressionInfo>&, T&)
492{
493 return false;
494}
495
496template <expressions::is_filter T>
497bool createExpressionTrees(std::vector<ExpressionInfo>& expressionInfos, T& filter)
498{
500 return true;
501}
502
503template <typename T>
505{
506 return false;
507}
508
509template <is_partition T>
510bool newDataframePartition(T& partition)
511{
512 partition.dataframeChanged = true;
513 return true;
514}
515
516template <typename P, typename... T>
517void setPartition(P&, T&...)
518{
519}
520
521template <is_partition P, typename... T>
522void setPartition(P& partition, T&... tables)
523{
524 ([&]() { if constexpr (std::same_as<typename P::content_t, T>) {partition.bindTable(tables);} }(), ...);
525}
526
527template <typename P, typename T>
529{
530}
531
532template <is_partition P, typename T>
533void bindInternalIndicesPartition(P& partition, T* table)
534{
535 if constexpr (o2::soa::is_binding_compatible_v<typename P::content_t, std::decay_t<T>>()) {
536 partition.bindInternalIndicesTo(table);
537 }
538}
539
540template <typename P, typename... T>
542{
543}
544
545template <is_partition P, typename... T>
546void bindExternalIndicesPartition(P& partition, T*... tables)
547{
548 partition.bindExternalIndices(tables...);
549}
550
552template <typename T>
554{
555 return false;
556}
557
558template <typename T>
560{
561 return false;
562}
563
564template <is_slice_cache T>
565bool initializeCache(ProcessingContext& context, T& cache)
566{
567 if (cache.ptr == nullptr) {
568 cache.ptr = &context.services().get<ArrowTableSlicingCache>();
569 }
570 return true;
571}
572
574template <typename C, typename TG, typename... Ts>
575void setGroupedCombination(C&, TG&, Ts&...)
576{
577}
578
579template <is_combinations_generator C, typename TG, typename... Ts>
580static void setGroupedCombination(C& comb, TG& grouping, std::tuple<Ts...>& associated)
581{
582 if constexpr (std::same_as<typename C::g_t, std::decay_t<TG>>) {
583 comb.setTables(grouping, associated);
584 }
585}
586
588template <typename T>
589 requires(!is_preslice<T> && !is_preslice_group<T>)
591{
592 return false;
593}
594
595template <is_preslice T>
596 requires std::same_as<typename T::policy_t, framework::PreslicePolicySorted>
597bool registerCache(T& preslice, Cache& bsks, Cache&)
598{
599 if constexpr (T::optional) {
600 if (preslice.binding == "[MISSING]") {
601 return true;
602 }
603 }
604 auto locate = std::find_if(bsks.begin(), bsks.end(), [&](auto const& entry) { return (entry.binding == preslice.bindingKey.binding) && (entry.key == preslice.bindingKey.key); });
605 if (locate == bsks.end()) {
606 bsks.emplace_back(preslice.getBindingKey());
607 } else if (locate->enabled == false) {
608 locate->enabled = true;
609 }
610 return true;
611}
612
613template <is_preslice T>
614 requires std::same_as<typename T::policy_t, framework::PreslicePolicyGeneral>
615bool registerCache(T& preslice, Cache&, Cache& bsksU)
616{
617 if constexpr (T::optional) {
618 if (preslice.binding == "[MISSING]") {
619 return true;
620 }
621 }
622 auto locate = std::find_if(bsksU.begin(), bsksU.end(), [&](auto const& entry) { return (entry.binding == preslice.bindingKey.binding) && (entry.key == preslice.bindingKey.key); });
623 if (locate == bsksU.end()) {
624 bsksU.emplace_back(preslice.getBindingKey());
625 } else if (locate->enabled == false) {
626 locate->enabled = true;
627 }
628 return true;
629}
630
631template <is_preslice_group T>
632bool registerCache(T& presliceGroup, Cache& bsks, Cache& bsksU)
633{
634 homogeneous_apply_refs<true>([&bsks, &bsksU](auto& preslice) { return registerCache(preslice, bsks, bsksU); }, presliceGroup);
635 return true;
636}
637
638template <typename T>
641{
642 return false;
643}
644
645template <is_preslice T>
646static bool updateSliceInfo(T& preslice, ArrowTableSlicingCache& cache)
647 requires std::same_as<typename T::policy_t, framework::PreslicePolicySorted>
648{
649 if constexpr (T::optional) {
650 if (preslice.binding == "[MISSING]") {
651 return true;
652 }
653 }
654 preslice.updateSliceInfo(cache.getCacheFor(preslice.getBindingKey()));
655 return true;
656}
657
658template <is_preslice T>
659static bool updateSliceInfo(T& preslice, ArrowTableSlicingCache& cache)
660 requires std::same_as<typename T::policy_t, framework::PreslicePolicyGeneral>
661{
662 if constexpr (T::optional) {
663 if (preslice.binding == "[MISSING]") {
664 return true;
665 }
666 }
667 preslice.updateSliceInfo(cache.getCacheUnsortedFor(preslice.getBindingKey()));
668 return true;
669}
670
671template <is_preslice_group T>
672static bool updateSliceInfo(T& presliceGroup, ArrowTableSlicingCache& cache)
673{
674 homogeneous_apply_refs<true>([&cache](auto& preslice) { return updateSliceInfo(preslice, cache); }, presliceGroup);
675 return true;
676}
677
679template <typename T>
680static bool setProcessSwitch(std::pair<std::string, bool>, T&)
681{
682 return false;
683}
684
685template <is_process_configurable T>
686static bool setProcessSwitch(std::pair<std::string, bool> setting, T& pc)
687{
688 if (pc.name == setting.first) {
689 pc.value = setting.second;
690 return true;
691 }
692 return false;
693}
694
695} // namespace analysis_task_parsers
696} // namespace o2::framework
697
698#endif // ANALYSISMANAGERS_H
constexpr uint32_t runtime_hash(char const *str)
void snapshot(const Output &spec, T const &object)
decltype(auto) make(const Output &spec, Args... args)
ServiceRegistryRef services()
Definition InitContext.h:34
ConfigParamRegistry const & options()
Definition InitContext.h:33
The input API of the Data Processing Layer This class holds the inputs which are valid for processing...
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
ServiceRegistryRef services()
The services registry associated with this processing context.
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
bool prepareService(InitContext &, T &)
bool requestInputs(std::vector< InputSpec > &, T const &)
Table auto-creation handling.
void setGroupedCombination(C &, TG &, Ts &...)
Combinations handling.
bool initializeCache(ProcessingContext &, T &)
bool preInitializeCache(InitContext &, T &)
Cache handling.
bool prepareDelayedOutput(ProcessingContext &, T &)
bool prepareOption(InitContext &, O &)
bool appendOutput(std::vector< OutputSpec > &, T &, uint32_t)
Outputs handling.
bool finalizeOutput(ProcessingContext &, T &)
bool registerCache(T &, Cache &, Cache &)
Preslice handling.
bool postRunOutput(EndOfStreamContext &, T &)
bool createExpressionTrees(std::vector< ExpressionInfo > &, T &)
bool prepareOutput(ProcessingContext &, T &)
bool appendOption(std::vector< ConfigParamSpec > &, O &)
Options handling.
bool updateSliceInfo(T &, ArrowTableSlicingCache &)
bool appendCondition(std::vector< InputSpec > &, C &)
Conditions handling.
bool postRunService(EndOfStreamContext &, T &)
bool addService(std::vector< ServiceSpec > &, T &)
Service handling.
bool updatePlaceholders(InitContext &, T &)
Filter handling.
bool newDataframeCondition(InputRecord &, C &)
void updateExpressionInfos(expressions::Filter const &filter, std::vector< ExpressionInfo > &eInfos)
Function for attaching gandiva filters to to compatible task inputs.
void updatePlaceholders(Filter &filter, InitContext &context)
Update placeholder nodes from context.
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
std::vector< Entry > Cache
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
static bool appendOption(std::vector< ConfigParamSpec > &options, Configurable< T, K, IP > &what)
static void updateInputList(std::vector< InputSpec > &list, InputSpec &&input)
Updates list of InputSpecs by merging metadata.
std::vector< OutputRoute > outputs
Definition DeviceSpec.h:63
static std::vector< LoadablePlugin > parsePluginSpecString(char const *str)
Parse a comma separated list of <library>:<plugin-name> plugin declarations.
static std::shared_ptr< arrow::Table > joinTables(std::vector< std::shared_ptr< arrow::Table > > &&tables, std::span< const char *const > labels)
Definition ASoA.cxx:72