MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
OperationChain.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Yantra {
6
7template <ComputeData InputType, ComputeData OutputType>
8class FunctionalOperation;
9
10namespace detail {
11 template <ComputeData In, ComputeData Out>
13 std::false_type functional_operation_base(...);
14
15 template <typename T>
16 struct datum_data;
17
18 template <ComputeData T>
19 struct datum_data<Datum<T>> {
20 using type = T;
21 };
22}
23
24/**
25 * @concept FunctionalOperationOn
26 * @brief OpClass derives from a FunctionalOperation whose input is DataType.
27 */
28template <typename OpClass, typename DataType>
29concept FunctionalOperationOn = decltype(detail::functional_operation_base(std::declval<OpClass*>()))::value
30 && std::same_as<typename OpClass::input_type, Datum<DataType>>;
31
32/**
33 * @class FluentExecutor
34 * @brief Fluent interface for chaining operations on any executor
35 *
36 * Provides a composable, type-safe way to chain operations together.
37 * This class is executor-agnostic and can work with any executor that
38 * exposes execute<OpClass, InputType, OutputType>(Datum<InputType>) and
39 * execute_named<OpClass, InputType, OutputType>(name, Datum<InputType>).
40 *
41 * Internal storage is always Datum<DataType>, preserving container
42 * references and structural metadata across every link in the chain.
43 * Entry points (constructors, make_fluent) accept raw DataType as a
44 * convenience and wrap it immediately.
45 *
46 * Key Features:
47 * - Type-safe operation chaining with compile-time verification
48 * - Support for both type-based and named operations
49 * - Container and metadata preservation through the full chain
50 * - Custom function application within the chain
51 * - Multiple terminal operations for different use cases
52 * - Error accumulation and reporting
53 *
54 * Usage:
55 * ```cpp
56 * auto result = matrix->with(input_datum)
57 * .then<SegmentOp>("segment")
58 * .then<AttributeOp>("attribute")
59 * .then<SortOp>("sort")
60 * .get();
61 * ```
62 */
63template <typename Executor, ComputeData DataType>
64class MAYAFLUX_API FluentExecutor {
65public:
66 using executor_type = Executor;
67 using data_type = DataType;
69
70 // ------------------------------------------------------------------
71 // Constructors — Datum overloads are primary; raw overloads wrap
72 // ------------------------------------------------------------------
73
74 /**
75 * @brief Construct with executor and Datum input
76 * @param executor Shared pointer to the executor instance
77 * @param input Datum carrying data, container, and structural metadata
78 */
79 FluentExecutor(std::shared_ptr<Executor> executor, const Datum<DataType>& input)
80 : m_executor(std::move(executor))
81 , m_data(input)
82 , m_successful(true)
83 {
84 if (!m_executor) {
85 error<std::invalid_argument>(
86 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
87 std::source_location::current(),
88 "FluentExecutor requires non-null executor");
89 }
90 }
91
92 /**
93 * @brief Construct with executor and Datum input (move)
94 * @param executor Shared pointer to the executor instance
95 * @param input Datum to move into the chain
96 */
97 FluentExecutor(std::shared_ptr<Executor> executor, Datum<DataType>&& input)
98 : m_executor(std::move(executor))
99 , m_data(std::move(input))
100 , m_successful(true)
101 {
102 if (!m_executor) {
103 error<std::invalid_argument>(
104 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
105 std::source_location::current(),
106 "FluentExecutor requires non-null executor");
107 }
108 }
109
110 /**
111 * @brief Construct with executor and raw data (convenience entry point)
112 *
113 * Wraps data in a Datum immediately. No container is attached; use the
114 * Datum overload when a container must be preserved through the chain.
115 *
116 * @param executor Shared pointer to the executor instance
117 * @param input Raw data to wrap and process
118 */
119 FluentExecutor(std::shared_ptr<Executor> executor, const DataType& input)
120 : FluentExecutor(std::move(executor), Datum<DataType>(input))
121 {
122 }
123
124 /**
125 * @brief Construct with executor and raw data (move, convenience entry point)
126 *
127 * Wraps data in a Datum immediately. No container is attached; use the
128 * Datum overload when a container must be preserved through the chain.
129 *
130 * @param executor Shared pointer to the executor instance
131 * @param input Raw data to move and wrap
132 */
133 FluentExecutor(std::shared_ptr<Executor> executor, DataType&& input)
134 : FluentExecutor(std::move(executor), Datum<DataType>(std::move(input)))
135 {
136 }
137
138 // ------------------------------------------------------------------
139 // Chain operations
140 // ------------------------------------------------------------------
141
142 /**
143 * @brief Chain an anonymous operation instance by type
144 *
145 * Constructs a new instance of OpClass and executes it on the current
146 * Datum. The full Datum (data + container + metadata) is passed through.
147 *
148 * @tparam OpClass Operation class to instantiate and execute
149 * @tparam OutputType Expected output type (defaults to current DataType)
150 * @return New FluentExecutor carrying the result Datum
151 */
152 template <typename OpClass, ComputeData OutputType = DataType>
155 {
156 if (!m_successful) {
157 error<std::runtime_error>(
158 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
159 std::source_location::current(),
160 "Cannot continue chain after failed operation");
161 }
162
163 try {
164 auto result = m_executor->template execute<OpClass, DataType, OutputType>(m_data);
165 if (!result) {
166 m_successful = false;
167 record_error("Operation " + std::string(typeid(OpClass).name()) + " failed");
168 error<std::runtime_error>(
169 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
170 std::source_location::current(),
171 "Operation failed in fluent chain: {}", std::string(typeid(OpClass).name()));
172 }
173
174 auto next = FluentExecutor<Executor, OutputType>(m_executor, std::move(*result));
175 next.m_operation_history = m_operation_history;
176 next.m_operation_history.push_back(typeid(OpClass).name());
177 return next;
178 } catch (const std::exception& e) {
179 m_successful = false;
180 record_error(e.what());
181 error_rethrow(
182 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
183 std::source_location::current(),
184 "Exception in fluent chain: " + std::string(e.what()));
185 }
186 }
187
188 /**
189 * @brief Chain a named operation fetched from the executor's pool
190 *
191 * Retrieves the pre-configured operation instance registered under @p name
192 * and executes it on the current Datum. The full Datum is passed through,
193 * preserving container and metadata.
194 *
195 * @tparam OpClass Operation class type
196 * @tparam OutputType Expected output type (defaults to current DataType)
197 * @param name Name of the operation in the pool
198 * @return New FluentExecutor carrying the result Datum
199 */
200 template <typename OpClass, ComputeData OutputType = DataType>
203 {
204 if (!m_successful) {
205 error<std::runtime_error>(
206 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
207 std::source_location::current(),
208 "Cannot continue chain after failed operation: {}", name);
209 }
210
211 try {
212 auto result = m_executor->template execute_named<OpClass, DataType, OutputType>(name, m_data);
213 if (!result) {
214 m_successful = false;
215 record_error("Named operation '" + name + "' failed");
216 error<std::runtime_error>(
217 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
218 std::source_location::current(),
219 "Named operation failed in fluent chain: {}", name);
220 }
221
222 auto next = FluentExecutor<Executor, OutputType>(m_executor, std::move(*result));
223 next.m_operation_history = m_operation_history;
224 next.m_operation_history.push_back(name);
225 return next;
226 } catch (const std::exception& e) {
227 m_successful = false;
228 record_error(e.what());
229 error_rethrow(
230 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
231 std::source_location::current(),
232 "Exception in named operation '{}': {}", name, e.what());
233 }
234 }
235
236 /**
237 * @brief Chain a FunctionalOperation child as an annotation of the
238 * current Datum.
239 *
240 * The chain keeps its data, container and type. The operation's output
241 * metadata is merged into the chain's, and its output data is stored in
242 * the metadata under the operation's type name.
243 */
244 template <typename OpClass>
247 {
249
250 if (!m_successful) {
251 error<std::runtime_error>(
252 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
253 std::source_location::current(),
254 "Cannot continue chain after failed operation");
255 }
256
257 const std::string key = typeid(OpClass).name();
258 try {
259 auto result = m_executor->template execute<OpClass, DataType, OutputType>(m_data);
260 if (!result) {
261 m_successful = false;
262 record_error("Operation " + key + " failed");
263 error<std::runtime_error>(
264 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
265 std::source_location::current(),
266 "Operation failed in fluent chain: {}", key);
267 }
268
269 auto next = FluentExecutor<Executor, DataType>(m_executor, absorbed(std::move(*result), key));
270 next.m_operation_history = m_operation_history;
271 next.m_operation_history.push_back(key);
272 return next;
273 } catch (const std::exception& e) {
274 m_successful = false;
275 record_error(e.what());
276 error_rethrow(
277 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
278 std::source_location::current(),
279 "Exception in fluent chain: " + std::string(e.what()));
280 }
281 }
282
283 /**
284 * @brief Chain a named FunctionalOperation child from the executor's
285 * pool as an annotation of the current Datum.
286 *
287 * The chain keeps its data, container and type. The operation's output
288 * metadata is merged into the chain's, and its output data is stored in
289 * the metadata under @p name.
290 */
291 template <typename OpClass>
294 {
296
297 if (!m_successful) {
298 error<std::runtime_error>(
299 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
300 std::source_location::current(),
301 "Cannot continue chain after failed operation: {}", name);
302 }
303
304 try {
305 auto result = m_executor->template execute_named<OpClass, DataType, OutputType>(name, m_data);
306 if (!result) {
307 m_successful = false;
308 record_error("Named operation '" + name + "' failed");
309 error<std::runtime_error>(
310 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
311 std::source_location::current(),
312 "Named operation failed in fluent chain: {}", name);
313 }
314
315 auto next = FluentExecutor<Executor, DataType>(m_executor, absorbed(std::move(*result), name));
316 next.m_operation_history = m_operation_history;
317 next.m_operation_history.push_back(name);
318 return next;
319 } catch (const std::exception& e) {
320 m_successful = false;
321 record_error(e.what());
322 error_rethrow(
323 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
324 std::source_location::current(),
325 "Exception in named operation '{}': {}", name, e.what());
326 }
327 }
328
329 /**
330 * @brief Apply a custom transformation function within the chain
331 *
332 * @p func receives the raw @c DataType (not the Datum) and returns a
333 * value of any ComputeData type. The result is immediately wrapped in a
334 * fresh Datum. Container from the current Datum is not carried forward
335 * since the function may change the data type entirely; attach a container
336 * explicitly via tap() before apply() if it must survive.
337 *
338 * @tparam Func Callable type: (const DataType&) -> U
339 * @param func Transformation to apply
340 * @return New FluentExecutor whose DataType is the return type of @p func
341 */
342 template <typename Func>
343 requires std::invocable<Func, const DataType&>
344 auto apply(Func&& func)
345 {
346 if (!m_successful) {
347 error<std::runtime_error>(
348 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
349 std::source_location::current(),
350 "Cannot continue chain after failed operation in apply");
351 }
352
353 using ResultType = std::invoke_result_t<Func, const DataType&>;
354
355 try {
356 auto result = std::forward<Func>(func)(m_data.data);
358 m_executor, Datum<ResultType>(std::move(result)));
359 next.m_operation_history = m_operation_history;
360 next.m_operation_history.push_back("custom_function");
361 return next;
362 } catch (const std::exception& e) {
363 m_successful = false;
364 record_error(std::string("Custom function failed: ") + e.what());
365 error_rethrow(
366 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
367 std::source_location::current(),
368 "Exception in custom function: " + std::string(e.what()));
369 }
370 }
371
372 /**
373 * @brief Apply a side-effect function without changing data or type
374 *
375 * @p func receives a mutable reference to the raw @c DataType. Useful
376 * for logging, validation, or in-place mutation that does not change type.
377 * Does not affect the Datum wrapper or container.
378 *
379 * @tparam Func Callable type: (DataType&) -> void
380 * @param func Function to apply for side effects
381 * @return Reference to this executor for continued chaining
382 */
383 template <typename Func>
384 requires std::invocable<Func, DataType&>
385 FluentExecutor& tap(Func&& func)
386 {
387 if (!m_successful) {
388 error<std::runtime_error>(
389 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
390 std::source_location::current(),
391 "Cannot continue chain after failed operation in tap");
392 }
393
394 try {
395 std::forward<Func>(func)(m_data.data);
396 m_operation_history.emplace_back("tap");
397 return *this;
398 } catch (const std::exception& e) {
399 m_successful = false;
400 record_error(std::string("Tap function failed: ") + e.what());
401 error_rethrow(
402 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
403 std::source_location::current(),
404 "Exception in tap function: " + std::string(e.what()));
405 }
406 }
407
408 // ------------------------------------------------------------------
409 // Conditional execution
410 // ------------------------------------------------------------------
411
412 /**
413 * @brief Execute an operation conditionally on a boolean flag
414 * @tparam OpClass Operation to execute if @p condition is true
415 * @param condition If true, executes the operation; otherwise passes through
416 * @return Reference to this executor for continued chaining
417 */
418 template <typename OpClass>
419 FluentExecutor& when(bool condition)
420 {
421 if (condition && m_successful)
422 return then<OpClass>();
423 return *this;
424 }
425
426 /**
427 * @brief Execute an operation conditionally on a predicate over the raw data
428 * @tparam OpClass Operation to execute if @p predicate returns true
429 * @tparam Pred Callable type: (const DataType&) -> bool
430 * @param predicate Function evaluated against the current raw data
431 * @return Reference to this executor for continued chaining
432 */
433 template <typename OpClass, typename Pred>
434 requires std::predicate<Pred, const DataType&>
435 FluentExecutor& when(Pred&& predicate)
436 {
437 if (m_successful && std::forward<Pred>(predicate)(m_data.data))
438 return then<OpClass>();
439 return *this;
440 }
441
442 // ------------------------------------------------------------------
443 // Parallel fork
444 // ------------------------------------------------------------------
445
446 /**
447 * @brief Fork execution into multiple independent parallel paths
448 *
449 * Executes each operation in @p OpClasses against the current Datum
450 * concurrently and returns a tuple of results. Each result is an
451 * @c std::optional<Datum<DataType>>.
452 *
453 * @tparam OpClasses Operation classes to execute in parallel
454 * @return std::tuple of optional Datum results, one per OpClass
455 */
456 template <typename... OpClasses>
457 auto fork()
458 {
459 if (!m_successful) {
460 error<std::runtime_error>(
461 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
462 std::source_location::current(),
463 "Cannot fork after failed operation");
464 }
465
466 return std::make_tuple(
467 m_executor->template execute<OpClasses, DataType>(m_data)...);
468 }
469
470 // ------------------------------------------------------------------
471 // Terminal accessors
472 // ------------------------------------------------------------------
473
474 /**
475 * @brief Get the final raw result by const reference
476 * @return Const reference to the processed DataType
477 */
478 [[nodiscard]] const DataType& get() const
479 {
480 if (!m_successful) {
481 error<std::runtime_error>(
482 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
483 std::source_location::current(),
484 "Cannot get result from failed chain");
485 }
486 return m_data.data;
487 }
488
489 /**
490 * @brief Get a mutable reference to the final raw result
491 * @return Mutable reference to the processed DataType
492 */
493 DataType& get_mutable()
494 {
495 if (!m_successful) {
496 error<std::runtime_error>(
497 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
498 std::source_location::current(),
499 "Cannot get mutable result from failed chain");
500 }
501 return m_data.data;
502 }
503
504 /**
505 * @brief Move the final raw result out of the chain
506 * @return Moved DataType
507 */
508 DataType consume() &&
509 {
510 if (!m_successful) {
511 error<std::runtime_error>(
512 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
513 std::source_location::current(),
514 "Cannot consume result from failed chain");
515 }
516 return std::move(m_data.data);
517 }
518
519 /**
520 * @brief Get the full result Datum by const reference
521 *
522 * Prefer this over get() when the container or metadata must be
523 * inspected or passed to a subsequent non-chain call.
524 *
525 * @return Const reference to the result Datum
526 */
527 [[nodiscard]] const Datum<DataType>& get_datum() const
528 {
529 if (!m_successful) {
530 error<std::runtime_error>(
531 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
532 std::source_location::current(),
533 "Cannot get datum from failed chain");
534 }
535 return m_data;
536 }
537
538 /**
539 * @brief Move the full result Datum out of the chain
540 * @return Moved Datum including container and metadata
541 */
543 {
544 if (!m_successful) {
545 error<std::runtime_error>(
546 Journal::Component::Yantra, Journal::Context::ComputeMatrix,
547 std::source_location::current(),
548 "Cannot consume datum from failed chain");
549 }
550 return std::move(m_data);
551 }
552
553 /**
554 * @brief Return the result Datum with execution history appended to metadata
555 *
556 * Equivalent to get_datum() but stamps execution_history, successful,
557 * and errors into the Datum metadata before returning.
558 *
559 * @return Datum copy with execution metadata attached
560 */
561 [[nodiscard]] Datum<DataType> to_io() const
562 {
563 Datum<DataType> result = m_data;
564 result.metadata["execution_history"] = m_operation_history;
565 result.metadata["successful"] = m_successful;
566 if (!m_errors.empty())
567 result.metadata["errors"] = m_errors;
568 return result;
569 }
570
571 /**
572 * @brief Return the raw result or a fallback value if the chain failed
573 * @param default_value Value returned when the chain is in a failed state
574 * @return Processed DataType or @p default_value
575 */
576 [[nodiscard]] DataType get_or(const DataType& default_value) const
577 {
578 return m_successful ? m_data.data : default_value;
579 }
580
581 /**
582 * @brief Return the raw result or invoke a generator if the chain failed
583 * @tparam Generator Callable type: () -> DataType
584 * @param generator Invoked to produce a fallback when the chain failed
585 * @return Processed DataType or result of @p generator
586 */
587 template <typename Generator>
588 requires std::invocable<Generator>
589 [[nodiscard]] DataType get_or_else(Generator&& generator) const
590 {
591 return m_successful ? m_data.data : std::forward<Generator>(generator)();
592 }
593
594 // ------------------------------------------------------------------
595 // Reset
596 // ------------------------------------------------------------------
597
598 /**
599 * @brief Reset the chain with a new Datum, clearing history and errors
600 *
601 * Primary reset overload. Replaces internal state with @p new_datum,
602 * restoring the chain to a successful state ready for reuse.
603 *
604 * @param new_datum Datum to restart the chain from
605 * @return Reference to this executor for continued chaining
606 */
608 {
609 m_data = new_datum;
610 m_successful = true;
611 m_operation_history.clear();
612 m_errors.clear();
613 return *this;
614 }
615
616 /**
617 * @brief Reset the chain with raw data (convenience overload)
618 *
619 * Wraps @p new_data in a Datum and delegates to the primary reset.
620 * No container is attached; use the Datum overload if a container
621 * must be present from the start of the next chain.
622 *
623 * @param new_data Raw data to wrap and restart the chain from
624 * @return Reference to this executor for continued chaining
625 */
626 FluentExecutor& reset(const DataType& new_data)
627 {
628 return reset(Datum<DataType>(new_data));
629 }
630
631 // ------------------------------------------------------------------
632 // Accessors
633 // ------------------------------------------------------------------
634
635 /**
636 * @brief Check whether all operations in the chain have succeeded
637 * @return true if no operation has failed
638 */
639 [[nodiscard]] bool is_successful() const { return m_successful; }
640
641 /**
642 * @brief Get the ordered list of operation names executed so far
643 * @return Const reference to the operation history vector
644 */
645 [[nodiscard]] const std::vector<std::string>& get_history() const { return m_operation_history; }
646
647 /**
648 * @brief Get all errors accumulated during the chain
649 * @return Const reference to the error message vector
650 */
651 [[nodiscard]] const std::vector<std::string>& get_errors() const { return m_errors; }
652
653 /**
654 * @brief Get the underlying executor
655 * @return Shared pointer to the executor instance
656 */
657 [[nodiscard]] std::shared_ptr<Executor> get_executor() const { return m_executor; }
658
659private:
660 std::shared_ptr<Executor> m_executor;
663 std::vector<std::string> m_operation_history;
664 std::vector<std::string> m_errors;
665
666 template <typename E, ComputeData U>
667 friend class FluentExecutor;
668
669 void record_error(const std::string& err) { m_errors.push_back(err); }
670
671 /**
672 * @brief The current Datum with @p result's metadata merged in and its
673 * data stored under @p key.
674 */
675 template <ComputeData OutputType>
676 [[nodiscard]] Datum<DataType> absorbed(Datum<OutputType>&& result, const std::string& key) const
677 {
678 Datum<DataType> next = m_data;
679 for (auto& [k, v] : result.metadata)
680 next.metadata.insert_or_assign(k, std::move(v));
681 next.metadata.insert_or_assign(key, std::move(result.data));
682 return next;
683 }
684};
685
686// ------------------------------------------------------------------
687// make_fluent — free function entry points
688// ------------------------------------------------------------------
689
690/**
691 * @brief Construct a FluentExecutor from a Datum with type deduction
692 * @tparam Executor Executor type
693 * @tparam DataType Inner data type (deduced from Datum)
694 * @param executor Shared pointer to the executor
695 * @param datum Datum to start the chain from
696 * @return FluentExecutor<Executor, DataType>
697 */
698template <typename Executor, ComputeData DataType>
699auto make_fluent(std::shared_ptr<Executor> executor, Datum<DataType>&& datum)
700{
702 std::move(executor),
703 std::forward<Datum<DataType>>(datum));
704}
705
706/**
707 * @brief Construct a FluentExecutor from raw data with type deduction (convenience entry point)
708 *
709 * Wraps @p data in a Datum immediately. No container is attached. Use the
710 * Datum overload when a container must survive through the chain.
711 *
712 * @tparam Executor Executor type
713 * @tparam DataType Data type (deduced)
714 * @param executor Shared pointer to the executor
715 * @param data Raw data to wrap and start the chain from
716 * @return FluentExecutor<Executor, decay_t<DataType>>
717 */
718template <typename Executor, ComputeData DataType>
719auto make_fluent(std::shared_ptr<Executor> executor, DataType&& data)
720{
722 std::move(executor),
723 Datum<std::decay_t<DataType>>(std::forward<DataType>(data)));
724}
725
726} // namespace MayaFlux::Yantra
Core::GlobalInputConfig input
Definition Config.cpp:42
glm::vec3 value
std::string name
Definition VKDevice.cpp:123
auto fork()
Fork execution into multiple independent parallel paths.
FluentExecutor(std::shared_ptr< Executor > executor, DataType &&input)
Construct with executor and raw data (move, convenience entry point)
FluentExecutor & when(Pred &&predicate)
Execute an operation conditionally on a predicate over the raw data.
FluentExecutor & reset(const DataType &new_data)
Reset the chain with raw data (convenience overload)
const std::vector< std::string > & get_history() const
Get the ordered list of operation names executed so far.
Datum< DataType > consume_datum() &&
Move the full result Datum out of the chain.
void record_error(const std::string &err)
FluentExecutor & tap(Func &&func)
Apply a side-effect function without changing data or type.
std::shared_ptr< Executor > get_executor() const
Get the underlying executor.
DataType get_or(const DataType &default_value) const
Return the raw result or a fallback value if the chain failed.
FluentExecutor< Executor, OutputType > then(const std::string &name)
Chain a named operation fetched from the executor's pool.
auto apply(Func &&func)
Apply a custom transformation function within the chain.
DataType consume() &&
Move the final raw result out of the chain.
FluentExecutor(std::shared_ptr< Executor > executor, const Datum< DataType > &input)
Construct with executor and Datum input.
FluentExecutor & when(bool condition)
Execute an operation conditionally on a boolean flag.
FluentExecutor(std::shared_ptr< Executor > executor, Datum< DataType > &&input)
Construct with executor and Datum input (move)
std::shared_ptr< Executor > m_executor
const std::vector< std::string > & get_errors() const
Get all errors accumulated during the chain.
bool is_successful() const
Check whether all operations in the chain have succeeded.
DataType get_or_else(Generator &&generator) const
Return the raw result or invoke a generator if the chain failed.
const Datum< DataType > & get_datum() const
Get the full result Datum by const reference.
FluentExecutor< Executor, DataType > then()
Chain a FunctionalOperation child as an annotation of the current Datum.
std::vector< std::string > m_errors
FluentExecutor< Executor, DataType > then(const std::string &name)
Chain a named FunctionalOperation child from the executor's pool as an annotation of the current Datu...
Datum< DataType > absorbed(Datum< OutputType > &&result, const std::string &key) const
The current Datum with result's metadata merged in and its data stored under key.
FluentExecutor(std::shared_ptr< Executor > executor, const DataType &input)
Construct with executor and raw data (convenience entry point)
FluentExecutor & reset(const Datum< DataType > &new_datum)
Reset the chain with a new Datum, clearing history and errors.
DataType & get_mutable()
Get a mutable reference to the final raw result.
FluentExecutor< Executor, OutputType > then()
Chain an anonymous operation instance by type.
const DataType & get() const
Get the final raw result by const reference.
Datum< DataType > to_io() const
Return the result Datum with execution history appended to metadata.
std::vector< std::string > m_operation_history
Fluent interface for chaining operations on any executor.
Wraps a single callable as a ComputeOperation, for a wild or one-off idea that does not warrant a han...
OpClass derives from a FunctionalOperation whose input is DataType.
std::true_type functional_operation_base(const FunctionalOperation< In, Out > *)
auto make_fluent(std::shared_ptr< Executor > executor, Datum< DataType > &&datum)
Construct a FluentExecutor from a Datum with type deduction.
std::unordered_map< std::string, std::any > metadata
Associated metadata.
Definition DataIO.hpp:30
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:26