121 lines
4.4 KiB
C++
121 lines
4.4 KiB
C++
// Example 04 — Storage Policy
|
|
//
|
|
// Demonstrates how KPN++ chooses between by-value and shared_ptr storage
|
|
// inside channels depending on the type.
|
|
//
|
|
// Small trivially-copyable types (int, double, etc.) are stored by value.
|
|
// Large or non-trivially-copyable types are stored as shared_ptr<const T>
|
|
// — zero copies even across multiple downstream consumers.
|
|
//
|
|
// This example also shows how to override the policy for a specific type
|
|
// with a template specialisation.
|
|
//
|
|
// [produce_frame] --Frame--> [process_frame] --Frame--> [consume_frame]
|
|
// [produce_small] --int----> [double_it] --int----> [print_int]
|
|
|
|
#include <kpn/kpn.hpp>
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
|
|
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
|
|
// Large frame type — will be stored as shared_ptr<const Frame> by default
|
|
struct Frame {
|
|
std::array<uint8_t, 4096> pixels{};
|
|
int id = 0;
|
|
};
|
|
|
|
// A small struct we force to be stored by value via policy specialisation
|
|
struct Tag {
|
|
int value = 0;
|
|
};
|
|
|
|
// --8<-- [start:storage_policy_spec]
|
|
// Override: store Tag by value despite being a struct
|
|
// (it's trivially copyable and small — this just makes the policy explicit)
|
|
template<>
|
|
struct kpn::channel_storage_policy<Tag> {
|
|
static constexpr bool by_value = true;
|
|
};
|
|
// --8<-- [end:storage_policy_spec]
|
|
|
|
// ── Node functions ────────────────────────────────────────────────────────────
|
|
|
|
static int frame_id = 0;
|
|
static int tag_id = 0;
|
|
|
|
static Frame produce_frame() {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(40));
|
|
Frame f;
|
|
f.id = frame_id++;
|
|
f.pixels.fill(static_cast<uint8_t>(f.id & 0xFF));
|
|
return f;
|
|
}
|
|
|
|
static Frame process_frame(Frame f) {
|
|
// Simulate some work
|
|
for (auto& p : f.pixels) p = static_cast<uint8_t>(255 - p);
|
|
return f;
|
|
}
|
|
|
|
static void consume_frame(Frame f) {
|
|
std::cout << "[frame] id=" << f.id
|
|
<< " first_px=" << static_cast<int>(f.pixels[0])
|
|
<< " storage=shared_ptr (sizeof Frame = " << sizeof(Frame) << " B)\n";
|
|
}
|
|
|
|
static Tag produce_tag() {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(40));
|
|
return {tag_id++};
|
|
}
|
|
|
|
static Tag double_tag(Tag t) { return {t.value * 2}; }
|
|
|
|
static void print_tag(Tag t) {
|
|
std::cout << "[tag] value=" << t.value
|
|
<< " storage=by_value (sizeof Tag = " << sizeof(Tag) << " B)\n";
|
|
}
|
|
|
|
// ── main ──────────────────────────────────────────────────────────────────────
|
|
|
|
int main() {
|
|
using namespace kpn;
|
|
|
|
// Verify storage decisions at compile time
|
|
static_assert(!channel_storage_policy<Frame>::by_value,
|
|
"Frame should use shared_ptr storage");
|
|
static_assert(channel_storage_policy<Tag>::by_value,
|
|
"Tag should use by_value storage (via specialisation)");
|
|
static_assert(channel_storage_policy<int>::by_value,
|
|
"int should use by_value storage");
|
|
|
|
auto prod_f = make_node<produce_frame> (out<"frame">{}, 4);
|
|
auto proc_f = make_node<process_frame> (in<"frame">{}, out<"frame">{}, 4);
|
|
auto cons_f = make_node<consume_frame> (in<"frame">{}, 4);
|
|
|
|
auto prod_t = make_node<produce_tag> (out<"tag">{}, 4);
|
|
auto dbl_t = make_node<double_tag> (in<"tag">{}, out<"tag">{}, 4);
|
|
auto print_t = make_node<print_tag> (in<"tag">{}, 4);
|
|
|
|
Network net;
|
|
net.add("prod_f", prod_f)
|
|
.add("proc_f", proc_f)
|
|
.add("cons_f", cons_f)
|
|
.add("prod_t", prod_t)
|
|
.add("dbl_t", dbl_t)
|
|
.add("print_t", print_t)
|
|
.connect("prod_f", prod_f.template output<"frame">(), "proc_f", proc_f.template input<"frame">())
|
|
.connect("proc_f", proc_f.template output<"frame">(), "cons_f", cons_f.template input<"frame">())
|
|
.connect("prod_t", prod_t.template output<"tag">(), "dbl_t", dbl_t.template input<"tag">())
|
|
.connect("dbl_t", dbl_t.template output<"tag">(), "print_t", print_t.template input<"tag">())
|
|
.build();
|
|
|
|
net.start();
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(400));
|
|
net.stop();
|
|
}
|