diff --git a/public/og/moc-parity-matrix-reflection.png b/public/og/moc-parity-matrix-reflection.png new file mode 100644 index 00000000..d4a95c0d Binary files /dev/null and b/public/og/moc-parity-matrix-reflection.png differ diff --git a/public/og/reflection-dangling-signature.png b/public/og/reflection-dangling-signature.png index 9d591d40..2e551140 100644 Binary files a/public/og/reflection-dangling-signature.png and b/public/og/reflection-dangling-signature.png differ diff --git a/scripts/schedule_doubles.py b/scripts/schedule_doubles.py index 1987e2f3..e7c564a1 100644 --- a/scripts/schedule_doubles.py +++ b/scripts/schedule_doubles.py @@ -142,11 +142,11 @@ # runs beside the post of the day. "2026-10-28": {"slug": "qt-observable-models-known-limits", "hour": 16, "why": "follow-up to the Qt class template post, the known limits closed, beside the post of the day"}, - # A reflection lint that flags a dangling-view signature GCC 16.2 does not - # warn about, written in response to Klaus Iglberger's safety article of - # 18 October. It runs beside the API-contract post of the day. - "2026-10-29": {"slug": "reflection-dangling-signature", "hour": 16, - "why": "reflection signature lint, response to Iglberger's safety article, beside the API-contract post"}, + # The flagship of the Qt series: which rows of a moc conformance matrix reflection + # matches, and a class with no macro. It took the second slot of 29 October from + # the reflection signature lint, which moved to 2 November. + "2026-10-29": {"slug": "moc-parity-matrix-reflection", "hour": 16, + "why": "Qt series flagship beside the first post of the day"}, # P3533R2 (constexpr virtual inheritance) is accepted by GCC 16 and Clang # trunk, with one case that differs. It runs beside the P0963 post of the day. "2026-10-30": {"slug": "constexpr-virtual-inheritance", "hour": 16, @@ -162,6 +162,11 @@ "why": "Rust and Swift interop news alongside the evergreen post, moved from 22 October"}, # A GoogleTest news short. It moved from the first slot of 26 October, where the # post on the spec author's two corrections took the morning, to the first free second slot. + # A reflection lint that flags a dangling-view signature GCC 16.2 does not warn about, + # written in response to Klaus Iglberger's safety article of 18 October. It moved from + # 29 October, where the Qt flagship took the second slot. + "2026-11-02": {"slug": "reflection-dangling-signature", "hour": 16, + "why": "reflection signature lint, response to Iglberger's safety article, moved from 29 October"}, "2026-11-03": {"slug": "googletest-live-at-head", "hour": 16, "why": "GoogleTest release news alongside the first tidy-agent episode, moved from 26 October"}, } diff --git a/social/facebook/moc-parity-matrix-reflection/README.md b/social/facebook/moc-parity-matrix-reflection/README.md new file mode 100644 index 00000000..85dc2af2 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/README.md @@ -0,0 +1,130 @@ +# LinkedIn Square Post (1200x1200) + +Standard LinkedIn feed post at 1:1 square aspect ratio. Works well as a scroll-stopping image in the LinkedIn feed. + +## Usage + +```bash +brand-gen init social/linkedin-post my-post +cd my-post +# edit content.md +brand-gen build # -> index.html +brand-gen image # -> my-post.png (2400x2400 at 2x DPR) +``` + +## Directives + +All templates use one of four post types. Each uses the same directive pattern: + +### Quote + +```markdown +::::post{type=quote theme=dark logo=top-left} + +:::quote{author="Filip Sajdak" role="Founder & CEO, ScudoAI"} + +Sovereign AI is not a feature you bolt on later. It is the architecture. + +::: + +:::: +``` + +### Stat + +```markdown +::::post{type=stat theme=light accent=brand} + +:::stat{value="USD 80B" label="Worldwide sovereign cloud spend" context="Europe is the fastest-growing region at 83% YoY." source="Gartner, Feb 2026"} +::: + +:::: +``` + +### Announcement + +```markdown +::::post{type=announcement theme=light} + +:::announcement{kind="launch" date="April 2026"} + +# ScudoAI joins NVIDIA Inception + +We are thrilled to join the NVIDIA Inception program to accelerate sovereign AI deployment across European regulated industries. + +::: + +:::: +``` + +### Insight + +```markdown +::::post{type=insight theme=dark} + +:::insight{citation="EU AI Act full enforcement: 2 August 2026"} + +# The AI compliance deadline most companies are missing + +Full EU AI Act enforcement for high-risk AI systems begins in 2 August 2026. Most regulated enterprises are not ready for the documentation and audit requirements. Teams running cloud AI will face the hardest transition. + +::: + +:::: +``` + +## Attributes reference + +### `::::post` attributes + +- `type` - quote | stat | announcement | insight (required) +- `theme` - dark | light (default light) +- `accent` - brand | amber | nv (optional colored gradient background) +- `logo` - top-left | top-right | bottom-left | bottom-right (default bottom-right) + +### `:::quote` attributes + +- `author` - attribution name (appears below the quote) +- `role` - attribution role/title (appears under the author) + +### `:::stat` attributes + +- `value` - the big number (e.g. "USD 80B", "3/4", "Hours to minutes") +- `label` - short label under the value +- `context` - 1-2 sentences providing context +- `source` - small citation + +### `:::announcement` attributes + +- `kind` - launch | milestone | event | hire | partnership +- `date` - free-form date string + +### `:::insight` attributes + +- `citation` - small footer citation (e.g. regulation reference) + +## Character limits + +- **Quote body**: ~120 characters for maximum readability at thumbnail size +- **Stat label**: ~40 characters +- **Stat context**: ~200 characters +- **Announcement headline**: ~80 characters +- **Announcement body**: ~250 characters +- **Insight hook**: ~100 characters +- **Insight body**: ~300 characters + +Posts that exceed these limits will still render but typography may shrink or content may clip. + +## Media + +Add a `media/` directory to the project and reference local files: + +```markdown +:::background{image="nvidia-thor.jpg" overlay="dark"} +::: + +:::image{src="product-screenshot.png" alt="ScudoAI compliance dashboard" position="top"} +::: +``` + +Paths are relative to the project's `media/` directory. diff --git a/social/facebook/moc-parity-matrix-reflection/assets/brand-kit.json b/social/facebook/moc-parity-matrix-reflection/assets/brand-kit.json new file mode 100644 index 00000000..ea143d57 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/assets/brand-kit.json @@ -0,0 +1,11 @@ +{ + "template": "social/linkedin-post", + "bundleSource": "scudoai-social.css", + "brandKitVersion": "0.1.0", + "vendoredAt": "2026-10-24T12:45:59.134Z", + "files": [ + "scudoai.css", + "layout.css", + "logo-symbol.svg" + ] +} diff --git a/social/facebook/moc-parity-matrix-reflection/assets/layout.css b/social/facebook/moc-parity-matrix-reflection/assets/layout.css new file mode 100644 index 00000000..e69de29b diff --git a/social/facebook/moc-parity-matrix-reflection/assets/logo-symbol.svg b/social/facebook/moc-parity-matrix-reflection/assets/logo-symbol.svg new file mode 100644 index 00000000..1fc44966 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/assets/logo-symbol.svg @@ -0,0 +1,22 @@ + diff --git a/social/facebook/moc-parity-matrix-reflection/assets/scudoai.css b/social/facebook/moc-parity-matrix-reflection/assets/scudoai.css new file mode 100644 index 00000000..612b5a12 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/assets/scudoai.css @@ -0,0 +1,343 @@ +/* ============================================================ + * wro.cpp social card -- standalone stylesheet + * + * Self-contained styling for brand-gen's `social/linkedin-post` + * output. Replaces (does NOT extend) the scudoai-social.css that + * brand-gen ships with -- the design language here is the + * wro.cpp 'Editorial Magnet' look used by src/styles/global.css, + * not the scudoai blue-indigo deck aesthetic. + * + * Source of truth for tokens: src/styles/global.css:12-30 in the + * wrocpp.github.io repo. + * + * Brand-gen markup we style: + *
+ *
(decorative, hidden) + *
(decorative, hidden) + *
+ *

...

+ *

...

+ *
...
+ *
+ *
+ * + * ScudoAI (hidden; replaced via ::after) + *
+ *
+ * + * Other card types (card-quote, card-stat, card-announcement) + * get analogous rules at the bottom. + * ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +:root { + --paper: #FCFAF5; + --paper-2: #F3EFE6; + --paper-3: #E8E1D0; + --ink: #1B1B1D; + --ink-muted:#55555A; + --ink-faint:#8A8A90; + --rule: #D7D2C4; + --orange: #EA541E; + --orange-2: #D6481A; + --blue: #3EB5E9; + --blue-deep:#2E8FB8; + + --font-display: 'Quicksand', system-ui, -apple-system, sans-serif; + --font-body: 'Source Serif 4', Georgia, serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; +} + +html, body { + margin: 0; + padding: 0; + background: var(--paper); + color: var(--ink); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: var(--font-body); + font-size: 18px; + line-height: 1.6; +} + +/* Hide brand-gen decorative noise -- the wro.cpp aesthetic is calm + * and editorial, not a dark deck with glow + grid backgrounds. */ +.grid-bg, .glow { display: none !important; } + +/* ---------------- Card frame (1200x1200) ---------------- */ +.card { + position: relative; + width: 1200px; + height: 1200px; + background: var(--paper); + /* Faint warm radial wash, mirrors body { background-image } in + * src/styles/global.css:44-49 -- gives the paper texture without + * looking flat. */ + background-image: + radial-gradient(900px 700px at 80% -10%, rgba(234, 84, 30, 0.06), transparent 60%), + radial-gradient(800px 600px at -10% 110%, rgba(62, 181, 233, 0.05), transparent 60%); + overflow: hidden; + box-sizing: border-box; + padding: 130px 110px 130px 110px; + display: flex; + flex-direction: column; + /* Faint inner border gives the card a printed-page feel. */ + border-bottom: 6px solid var(--orange); +} + +/* Override: theme=dark cards still use paper. We keep the editorial + * light look across the board so the wro.cpp brand stays consistent. + * If the user really wants a dark variant later, this is the hook. */ +.card.dark { background: var(--paper) !important; color: var(--ink) !important; } +.card.dark h1, .card.dark h2, .card.dark p, +.card.dark blockquote { color: var(--ink) !important; } + +/* ---------------- INSIGHT card ---------------- */ +.card-insight { + justify-content: center; + padding-top: 200px; /* reserve space for logo watermark (top:90px + 64px + safety) */ +} +.card-insight .insight-content { + display: flex; + flex-direction: column; + gap: 60px; + max-width: 980px; +} +.card-insight h1 { + font-family: var(--font-display); + font-weight: 700; + font-size: 92pt; + line-height: 1.02; + letter-spacing: -0.025em; + color: var(--ink); + margin: 0; +} +.card-insight h1.title-long { + font-size: 72pt; + line-height: 1.06; + letter-spacing: -0.02em; + max-height: 420px; +} +.card-insight h1.title-xlong { + font-size: 56pt; + line-height: 1.10; + letter-spacing: -0.015em; + max-height: 440px; +} +/* Auto-accent: ++ in C++/C++26 turns orange, sentence-final period + * (when wrapped in ) turns blue. The skill writes + * the heading with these spans for the wro.cpp signature look. */ +.card-insight h1 .accent { color: var(--orange); } +.card-insight h1 .dot { color: var(--blue-deep); } +.card-insight p { + font-family: var(--font-body); + font-size: 30pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 0; + max-width: 920px; +} +.card-insight .insight-citation { + font-family: var(--font-mono); + font-size: 18pt; + letter-spacing: 0.04em; + color: var(--ink-faint); + margin-top: 40px; + padding-top: 30px; + border-top: 1px solid var(--rule); + text-transform: lowercase; +} +.card-insight .insight-citation::before { + content: '// '; + color: var(--orange); + font-weight: 700; +} + +/* ---------------- QUOTE card ---------------- */ +.card-quote { justify-content: center; padding: 140px 110px; } +.card-quote .quote-content { display: flex; flex-direction: column; align-items: flex-start; } +.card-quote .quote-mark { + font-family: var(--font-display); + font-weight: 700; + font-size: 200pt; + line-height: 0.8; + color: var(--orange); + opacity: 0.20; + /* In-flow above the text (not floated in the corner) with a negative + * bottom margin so the mark nearly touches the first quote line. */ + margin: 0 0 -0.62em; +} +.card-quote .quote-body { + font-family: var(--font-display); + font-weight: 600; + font-size: 64pt; + line-height: 1.15; + letter-spacing: -0.02em; + color: var(--ink); + max-width: 980px; + margin: 0; +} +.card-quote .quote-attribution { + margin-top: 50px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-muted); +} +.card-quote .quote-attribution::before { content: '-- '; color: var(--orange); } +/* author and role are adjacent spans with no separator in the markup; + * add one so they do not run together ("wro.cppC++26 ..."). */ +.card-quote .quote-attribution .role::before { content: ' / '; color: var(--ink-faint); } + +/* ---------------- STAT card ---------------- */ +.card-stat { justify-content: center; } +/* Value and label sit side by side (label wraps to stacked lines whose + * combined height roughly matches the value); context + source span the + * full width underneath. */ +.card-stat .stat-content { + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: + "value label" + "context context" + "source source"; + column-gap: 64px; + align-items: center; +} +.card-stat .stat-value { + grid-area: value; + font-family: var(--font-display); + font-weight: 700; + font-size: 240pt; + line-height: 0.82; + letter-spacing: -0.04em; + color: var(--orange); + margin: 0; +} +.card-stat .stat-label { + grid-area: label; + font-family: var(--font-display); + font-weight: 700; + font-size: 90pt; + line-height: 1.0; + letter-spacing: -0.02em; + color: var(--ink); + /* min-content forces a per-word wrap, so "codegen steps" stacks into + * two lines next to the value instead of running on one long line. */ + width: min-content; + margin: 0; +} +.card-stat .stat-context { + grid-area: context; + font-family: var(--font-body); + font-size: 26pt; + line-height: 1.45; + color: var(--ink-muted); + margin: 56px 0 0; + max-width: 880px; +} +.card-stat .stat-source { + grid-area: source; + margin-top: 44px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-faint); +} +.card-stat .stat-source::before { content: '// '; color: var(--orange); } + +/* ---------------- ANNOUNCEMENT card ---------------- */ +.card-announcement { justify-content: center; } +.card-announcement .announcement-meta { + font-family: var(--font-mono); + font-size: 20pt; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--orange); + margin-bottom: 30px; +} +.card-announcement .announcement-content h1, +.card-announcement .announcement-content h2 { + font-family: var(--font-display); + font-weight: 700; + font-size: 78pt; + line-height: 1.05; + letter-spacing: -0.02em; + color: var(--ink); + margin: 0; +} +.card-announcement .announcement-content p { + font-family: var(--font-body); + font-size: 28pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 40px 0 0; + max-width: 900px; +} + +/* ---------------- LOGO WATERMARK ---------------- */ +/* brand-gen hard-codes "ScudoAI" in .logo-wordmark; we hide it and + * draw "wro.cpp" via ::after on the watermark. The orange ".cpp" mirrors + * the brand-mark in the site header. */ +.logo-watermark { + position: absolute; + display: flex; + align-items: center; + gap: 18px; + height: 64px; + z-index: 10; + color: var(--ink); +} +.logo-watermark.top-left { top: 90px; left: 110px; } +.logo-watermark.top-right { top: 90px; right: 110px; } +.logo-watermark.bottom-left { bottom: 60px; left: 110px; } +.logo-watermark.bottom-right { bottom: 60px; right: 110px; } +.logo-watermark svg { + width: 64px; + height: 64px; + flex-shrink: 0; + /* Magnet logo PNG already has transparent paper-tone background; + * no rounded box needed -- it sits on the paper card directly. */ +} +.logo-wordmark { + font-size: 0 !important; + line-height: 0 !important; + /* Hide the upstream "ScudoAI" text node entirely. */ +} +.logo-watermark::after { + content: 'wro.cpp'; + font-family: var(--font-display); + font-weight: 700; + font-size: 28pt; + letter-spacing: -0.025em; + color: var(--ink); + line-height: 1; + /* The .cpp portion is given orange via a ::first-line trick below + * isn't reliable for inline -- so the SKILL writes the wordmark + * with explicit spans when it owns the markup. For this default + * rendering, the whole wordmark is ink and the orange comes through + * the bottom border + eyebrow. */ +} +.logo-watermark.top-right::after, +.logo-watermark.bottom-right::after { order: -1; margin-right: 18px; } + +/* ---------------- AI DISCLOSURE BADGE ---------------- */ +/* A small, honest "AI-GENERATED" label so the disclosure rides with the + * card image on social. Bottom-right corner (logo is top-left, citation is + * center-bottom, so this corner is free). Injected into index.html by + * inject.sh. See the /ai policy page. */ +.ai-badge { + position: absolute; + bottom: 74px; /* clears the 6px orange accent bar; sits in the bottom padding band */ + right: 110px; /* matches the card side padding / logo inset */ + z-index: 10; + font-family: var(--font-mono); + font-size: 14pt; + font-weight: 500; + letter-spacing: 0.10em; + text-transform: uppercase; + color: var(--ink-faint); +} + +/* ---------------- Defensive overrides ---------------- */ +/* Anything brand-gen might have set inline using its tokens. */ +* { box-sizing: border-box; } diff --git a/social/facebook/moc-parity-matrix-reflection/caption.md b/social/facebook/moc-parity-matrix-reflection/caption.md new file mode 100644 index 00000000..168a8117 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/caption.md @@ -0,0 +1,16 @@ +# Replacing moc with reflection: 29 of 47 matrix rows match, and a class needs no macro + +## Body +I listed 47 things moc does and tested each one against a real moc twin. 29 are the same (24 checked against a real moc), and a Qt class or a gadget now needs no macro at all. The open rows each have a stated reason. + +https://wrocpp.github.io/posts/moc-parity-matrix-reflection/ + +## Hashtags +#cpp #cpp26 #qt #moc #wrocpp + +## Alt-text +wro.cpp card with the magnet logo. Headline reads "A Qt class with no macro and no moc" with a subtitle about 29 of 47 rows of a moc conformance matrix being the same, 24 checked against a real moc. Citation footer: wro.cpp, 2026-10-29. + +## Suggested post time +Thursday 2026-10-29, 16:00 UTC +Reason: the second slot of the day, the flagship of the Qt series, a few days after the spec post. diff --git a/social/facebook/moc-parity-matrix-reflection/config.yaml b/social/facebook/moc-parity-matrix-reflection/config.yaml new file mode 100644 index 00000000..0fd5923b --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/config.yaml @@ -0,0 +1,12 @@ +template: social/linkedin-post + +size: + width: 1200 + height: 1200 + dpr: 2 + +project: + name: "moc-parity-matrix-reflection" + author: "Filip Sajdak, Founder & CEO" + email: "office@scudoai.com" + date: "2026-10-29" diff --git a/social/facebook/moc-parity-matrix-reflection/content.md b/social/facebook/moc-parity-matrix-reflection/content.md new file mode 100644 index 00000000..3712b236 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/content.md @@ -0,0 +1,12 @@ +--- +template: social/linkedin-post +--- + +::::post{type=insight theme=dark logo=top-left} + +:::insight{citation="wro.cpp -- 2026-10-29"} +# A Qt class with no macro and no moc +29 of 47 rows of a moc conformance matrix are the same, each with a test, and 24 are checked against a real moc. The 11 unsupported rows each carry a reason. +::: + +:::: diff --git a/social/facebook/moc-parity-matrix-reflection/image.png b/social/facebook/moc-parity-matrix-reflection/image.png new file mode 100644 index 00000000..4c1ec402 Binary files /dev/null and b/social/facebook/moc-parity-matrix-reflection/image.png differ diff --git a/social/facebook/moc-parity-matrix-reflection/index.html b/social/facebook/moc-parity-matrix-reflection/index.html new file mode 100644 index 00000000..1ec36cf6 --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/index.html @@ -0,0 +1,28 @@ + + + + + + moc-parity-matrix-reflection + + + + + + + + + + +

A Qt class with no macro and no moc

29 of 47 rows of a moc conformance matrix are the same, each with a test, and 24 are checked against a real moc. The 11 unsupported rows each carry a reason.

wro.cpp -- 2026-10-29
ScudoAI
AI-generated
+ + \ No newline at end of file diff --git a/social/facebook/moc-parity-matrix-reflection/media/.gitkeep b/social/facebook/moc-parity-matrix-reflection/media/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/social/facebook/moc-parity-matrix-reflection/media/README.md b/social/facebook/moc-parity-matrix-reflection/media/README.md new file mode 100644 index 00000000..bb81c7fa --- /dev/null +++ b/social/facebook/moc-parity-matrix-reflection/media/README.md @@ -0,0 +1,9 @@ +# Media directory + +Drop images here (PNG, JPG, SVG) for use in your post. + +Reference them in your content.md with: + + :::image{src="screenshot.png" alt="Alt text" position="top"} + +The renderer resolves paths relative to this media/ directory. diff --git a/social/facebook/reflection-dangling-signature/caption.md b/social/facebook/reflection-dangling-signature/caption.md index 4609b14d..43a67020 100644 --- a/social/facebook/reflection-dangling-signature/caption.md +++ b/social/facebook/reflection-dangling-signature/caption.md @@ -12,5 +12,5 @@ https://wrocpp.github.io/posts/reflection-dangling-signature/ Cream wro.cpp card with the magnet logo. Headline reads "Reflection sees the dangling view GCC 16.2 misses" with a subtitle saying a short lint flags a view returned from a by-value owner and -Wall -Wextra says nothing. Citation footer: wro.cpp, 2026-10-29. ## Suggested post time -Thursday 2026-10-29, 16:00 UTC -Reason: second post of the day; the API contract post holds the 08:00 UTC slot. +Monday 2026-11-02, 16:00 UTC +Reason: the second slot of the day, moved here from 29 October for the Qt flagship. diff --git a/social/facebook/reflection-dangling-signature/config.yaml b/social/facebook/reflection-dangling-signature/config.yaml index 8b745209..22293b63 100644 --- a/social/facebook/reflection-dangling-signature/config.yaml +++ b/social/facebook/reflection-dangling-signature/config.yaml @@ -9,4 +9,4 @@ project: name: "reflection-dangling-signature" author: "wro.cpp -- Wroclaw C++ community" email: "office@wro.cpp" - date: "2026-10-29" + date: "2026-11-02" diff --git a/social/facebook/reflection-dangling-signature/content.md b/social/facebook/reflection-dangling-signature/content.md index 80f30610..71a46880 100644 --- a/social/facebook/reflection-dangling-signature/content.md +++ b/social/facebook/reflection-dangling-signature/content.md @@ -4,7 +4,7 @@ template: social/linkedin-post ::::post{type=insight theme=dark logo=top-left} -:::insight{citation="wro.cpp -- 2026-10-29"} +:::insight{citation="wro.cpp -- 2026-11-02"} # Reflection sees the dangling view GCC 16.2 misses A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing. ::: diff --git a/social/facebook/reflection-dangling-signature/image.png b/social/facebook/reflection-dangling-signature/image.png index f01dde05..daa4ee77 100644 Binary files a/social/facebook/reflection-dangling-signature/image.png and b/social/facebook/reflection-dangling-signature/image.png differ diff --git a/social/facebook/reflection-dangling-signature/index.html b/social/facebook/reflection-dangling-signature/index.html index adf1257d..80e360ca 100644 --- a/social/facebook/reflection-dangling-signature/index.html +++ b/social/facebook/reflection-dangling-signature/index.html @@ -23,6 +23,6 @@ -

Reflection sees the dangling view GCC 16.2 misses

A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing.

wro.cpp -- 2026-10-29
ScudoAI
AI-generated
+

Reflection sees the dangling view GCC 16.2 misses

A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing.

wro.cpp -- 2026-11-02
ScudoAI
AI-generated
\ No newline at end of file diff --git a/social/linkedin/moc-parity-matrix-reflection/README.md b/social/linkedin/moc-parity-matrix-reflection/README.md new file mode 100644 index 00000000..85dc2af2 --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/README.md @@ -0,0 +1,130 @@ +# LinkedIn Square Post (1200x1200) + +Standard LinkedIn feed post at 1:1 square aspect ratio. Works well as a scroll-stopping image in the LinkedIn feed. + +## Usage + +```bash +brand-gen init social/linkedin-post my-post +cd my-post +# edit content.md +brand-gen build # -> index.html +brand-gen image # -> my-post.png (2400x2400 at 2x DPR) +``` + +## Directives + +All templates use one of four post types. Each uses the same directive pattern: + +### Quote + +```markdown +::::post{type=quote theme=dark logo=top-left} + +:::quote{author="Filip Sajdak" role="Founder & CEO, ScudoAI"} + +Sovereign AI is not a feature you bolt on later. It is the architecture. + +::: + +:::: +``` + +### Stat + +```markdown +::::post{type=stat theme=light accent=brand} + +:::stat{value="USD 80B" label="Worldwide sovereign cloud spend" context="Europe is the fastest-growing region at 83% YoY." source="Gartner, Feb 2026"} +::: + +:::: +``` + +### Announcement + +```markdown +::::post{type=announcement theme=light} + +:::announcement{kind="launch" date="April 2026"} + +# ScudoAI joins NVIDIA Inception + +We are thrilled to join the NVIDIA Inception program to accelerate sovereign AI deployment across European regulated industries. + +::: + +:::: +``` + +### Insight + +```markdown +::::post{type=insight theme=dark} + +:::insight{citation="EU AI Act full enforcement: 2 August 2026"} + +# The AI compliance deadline most companies are missing + +Full EU AI Act enforcement for high-risk AI systems begins in 2 August 2026. Most regulated enterprises are not ready for the documentation and audit requirements. Teams running cloud AI will face the hardest transition. + +::: + +:::: +``` + +## Attributes reference + +### `::::post` attributes + +- `type` - quote | stat | announcement | insight (required) +- `theme` - dark | light (default light) +- `accent` - brand | amber | nv (optional colored gradient background) +- `logo` - top-left | top-right | bottom-left | bottom-right (default bottom-right) + +### `:::quote` attributes + +- `author` - attribution name (appears below the quote) +- `role` - attribution role/title (appears under the author) + +### `:::stat` attributes + +- `value` - the big number (e.g. "USD 80B", "3/4", "Hours to minutes") +- `label` - short label under the value +- `context` - 1-2 sentences providing context +- `source` - small citation + +### `:::announcement` attributes + +- `kind` - launch | milestone | event | hire | partnership +- `date` - free-form date string + +### `:::insight` attributes + +- `citation` - small footer citation (e.g. regulation reference) + +## Character limits + +- **Quote body**: ~120 characters for maximum readability at thumbnail size +- **Stat label**: ~40 characters +- **Stat context**: ~200 characters +- **Announcement headline**: ~80 characters +- **Announcement body**: ~250 characters +- **Insight hook**: ~100 characters +- **Insight body**: ~300 characters + +Posts that exceed these limits will still render but typography may shrink or content may clip. + +## Media + +Add a `media/` directory to the project and reference local files: + +```markdown +:::background{image="nvidia-thor.jpg" overlay="dark"} +::: + +:::image{src="product-screenshot.png" alt="ScudoAI compliance dashboard" position="top"} +::: +``` + +Paths are relative to the project's `media/` directory. diff --git a/social/linkedin/moc-parity-matrix-reflection/assets/brand-kit.json b/social/linkedin/moc-parity-matrix-reflection/assets/brand-kit.json new file mode 100644 index 00000000..276d1b2d --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/assets/brand-kit.json @@ -0,0 +1,11 @@ +{ + "template": "social/linkedin-post", + "bundleSource": "scudoai-social.css", + "brandKitVersion": "0.1.0", + "vendoredAt": "2026-10-24T12:45:59.036Z", + "files": [ + "scudoai.css", + "layout.css", + "logo-symbol.svg" + ] +} diff --git a/social/linkedin/moc-parity-matrix-reflection/assets/layout.css b/social/linkedin/moc-parity-matrix-reflection/assets/layout.css new file mode 100644 index 00000000..e69de29b diff --git a/social/linkedin/moc-parity-matrix-reflection/assets/logo-symbol.svg b/social/linkedin/moc-parity-matrix-reflection/assets/logo-symbol.svg new file mode 100644 index 00000000..1fc44966 --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/assets/logo-symbol.svg @@ -0,0 +1,22 @@ + diff --git a/social/linkedin/moc-parity-matrix-reflection/assets/scudoai.css b/social/linkedin/moc-parity-matrix-reflection/assets/scudoai.css new file mode 100644 index 00000000..612b5a12 --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/assets/scudoai.css @@ -0,0 +1,343 @@ +/* ============================================================ + * wro.cpp social card -- standalone stylesheet + * + * Self-contained styling for brand-gen's `social/linkedin-post` + * output. Replaces (does NOT extend) the scudoai-social.css that + * brand-gen ships with -- the design language here is the + * wro.cpp 'Editorial Magnet' look used by src/styles/global.css, + * not the scudoai blue-indigo deck aesthetic. + * + * Source of truth for tokens: src/styles/global.css:12-30 in the + * wrocpp.github.io repo. + * + * Brand-gen markup we style: + *
+ *
(decorative, hidden) + *
(decorative, hidden) + *
+ *

...

+ *

...

+ *
...
+ *
+ *
+ * + * ScudoAI (hidden; replaced via ::after) + *
+ *
+ * + * Other card types (card-quote, card-stat, card-announcement) + * get analogous rules at the bottom. + * ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +:root { + --paper: #FCFAF5; + --paper-2: #F3EFE6; + --paper-3: #E8E1D0; + --ink: #1B1B1D; + --ink-muted:#55555A; + --ink-faint:#8A8A90; + --rule: #D7D2C4; + --orange: #EA541E; + --orange-2: #D6481A; + --blue: #3EB5E9; + --blue-deep:#2E8FB8; + + --font-display: 'Quicksand', system-ui, -apple-system, sans-serif; + --font-body: 'Source Serif 4', Georgia, serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; +} + +html, body { + margin: 0; + padding: 0; + background: var(--paper); + color: var(--ink); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: var(--font-body); + font-size: 18px; + line-height: 1.6; +} + +/* Hide brand-gen decorative noise -- the wro.cpp aesthetic is calm + * and editorial, not a dark deck with glow + grid backgrounds. */ +.grid-bg, .glow { display: none !important; } + +/* ---------------- Card frame (1200x1200) ---------------- */ +.card { + position: relative; + width: 1200px; + height: 1200px; + background: var(--paper); + /* Faint warm radial wash, mirrors body { background-image } in + * src/styles/global.css:44-49 -- gives the paper texture without + * looking flat. */ + background-image: + radial-gradient(900px 700px at 80% -10%, rgba(234, 84, 30, 0.06), transparent 60%), + radial-gradient(800px 600px at -10% 110%, rgba(62, 181, 233, 0.05), transparent 60%); + overflow: hidden; + box-sizing: border-box; + padding: 130px 110px 130px 110px; + display: flex; + flex-direction: column; + /* Faint inner border gives the card a printed-page feel. */ + border-bottom: 6px solid var(--orange); +} + +/* Override: theme=dark cards still use paper. We keep the editorial + * light look across the board so the wro.cpp brand stays consistent. + * If the user really wants a dark variant later, this is the hook. */ +.card.dark { background: var(--paper) !important; color: var(--ink) !important; } +.card.dark h1, .card.dark h2, .card.dark p, +.card.dark blockquote { color: var(--ink) !important; } + +/* ---------------- INSIGHT card ---------------- */ +.card-insight { + justify-content: center; + padding-top: 200px; /* reserve space for logo watermark (top:90px + 64px + safety) */ +} +.card-insight .insight-content { + display: flex; + flex-direction: column; + gap: 60px; + max-width: 980px; +} +.card-insight h1 { + font-family: var(--font-display); + font-weight: 700; + font-size: 92pt; + line-height: 1.02; + letter-spacing: -0.025em; + color: var(--ink); + margin: 0; +} +.card-insight h1.title-long { + font-size: 72pt; + line-height: 1.06; + letter-spacing: -0.02em; + max-height: 420px; +} +.card-insight h1.title-xlong { + font-size: 56pt; + line-height: 1.10; + letter-spacing: -0.015em; + max-height: 440px; +} +/* Auto-accent: ++ in C++/C++26 turns orange, sentence-final period + * (when wrapped in ) turns blue. The skill writes + * the heading with these spans for the wro.cpp signature look. */ +.card-insight h1 .accent { color: var(--orange); } +.card-insight h1 .dot { color: var(--blue-deep); } +.card-insight p { + font-family: var(--font-body); + font-size: 30pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 0; + max-width: 920px; +} +.card-insight .insight-citation { + font-family: var(--font-mono); + font-size: 18pt; + letter-spacing: 0.04em; + color: var(--ink-faint); + margin-top: 40px; + padding-top: 30px; + border-top: 1px solid var(--rule); + text-transform: lowercase; +} +.card-insight .insight-citation::before { + content: '// '; + color: var(--orange); + font-weight: 700; +} + +/* ---------------- QUOTE card ---------------- */ +.card-quote { justify-content: center; padding: 140px 110px; } +.card-quote .quote-content { display: flex; flex-direction: column; align-items: flex-start; } +.card-quote .quote-mark { + font-family: var(--font-display); + font-weight: 700; + font-size: 200pt; + line-height: 0.8; + color: var(--orange); + opacity: 0.20; + /* In-flow above the text (not floated in the corner) with a negative + * bottom margin so the mark nearly touches the first quote line. */ + margin: 0 0 -0.62em; +} +.card-quote .quote-body { + font-family: var(--font-display); + font-weight: 600; + font-size: 64pt; + line-height: 1.15; + letter-spacing: -0.02em; + color: var(--ink); + max-width: 980px; + margin: 0; +} +.card-quote .quote-attribution { + margin-top: 50px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-muted); +} +.card-quote .quote-attribution::before { content: '-- '; color: var(--orange); } +/* author and role are adjacent spans with no separator in the markup; + * add one so they do not run together ("wro.cppC++26 ..."). */ +.card-quote .quote-attribution .role::before { content: ' / '; color: var(--ink-faint); } + +/* ---------------- STAT card ---------------- */ +.card-stat { justify-content: center; } +/* Value and label sit side by side (label wraps to stacked lines whose + * combined height roughly matches the value); context + source span the + * full width underneath. */ +.card-stat .stat-content { + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: + "value label" + "context context" + "source source"; + column-gap: 64px; + align-items: center; +} +.card-stat .stat-value { + grid-area: value; + font-family: var(--font-display); + font-weight: 700; + font-size: 240pt; + line-height: 0.82; + letter-spacing: -0.04em; + color: var(--orange); + margin: 0; +} +.card-stat .stat-label { + grid-area: label; + font-family: var(--font-display); + font-weight: 700; + font-size: 90pt; + line-height: 1.0; + letter-spacing: -0.02em; + color: var(--ink); + /* min-content forces a per-word wrap, so "codegen steps" stacks into + * two lines next to the value instead of running on one long line. */ + width: min-content; + margin: 0; +} +.card-stat .stat-context { + grid-area: context; + font-family: var(--font-body); + font-size: 26pt; + line-height: 1.45; + color: var(--ink-muted); + margin: 56px 0 0; + max-width: 880px; +} +.card-stat .stat-source { + grid-area: source; + margin-top: 44px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-faint); +} +.card-stat .stat-source::before { content: '// '; color: var(--orange); } + +/* ---------------- ANNOUNCEMENT card ---------------- */ +.card-announcement { justify-content: center; } +.card-announcement .announcement-meta { + font-family: var(--font-mono); + font-size: 20pt; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--orange); + margin-bottom: 30px; +} +.card-announcement .announcement-content h1, +.card-announcement .announcement-content h2 { + font-family: var(--font-display); + font-weight: 700; + font-size: 78pt; + line-height: 1.05; + letter-spacing: -0.02em; + color: var(--ink); + margin: 0; +} +.card-announcement .announcement-content p { + font-family: var(--font-body); + font-size: 28pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 40px 0 0; + max-width: 900px; +} + +/* ---------------- LOGO WATERMARK ---------------- */ +/* brand-gen hard-codes "ScudoAI" in .logo-wordmark; we hide it and + * draw "wro.cpp" via ::after on the watermark. The orange ".cpp" mirrors + * the brand-mark in the site header. */ +.logo-watermark { + position: absolute; + display: flex; + align-items: center; + gap: 18px; + height: 64px; + z-index: 10; + color: var(--ink); +} +.logo-watermark.top-left { top: 90px; left: 110px; } +.logo-watermark.top-right { top: 90px; right: 110px; } +.logo-watermark.bottom-left { bottom: 60px; left: 110px; } +.logo-watermark.bottom-right { bottom: 60px; right: 110px; } +.logo-watermark svg { + width: 64px; + height: 64px; + flex-shrink: 0; + /* Magnet logo PNG already has transparent paper-tone background; + * no rounded box needed -- it sits on the paper card directly. */ +} +.logo-wordmark { + font-size: 0 !important; + line-height: 0 !important; + /* Hide the upstream "ScudoAI" text node entirely. */ +} +.logo-watermark::after { + content: 'wro.cpp'; + font-family: var(--font-display); + font-weight: 700; + font-size: 28pt; + letter-spacing: -0.025em; + color: var(--ink); + line-height: 1; + /* The .cpp portion is given orange via a ::first-line trick below + * isn't reliable for inline -- so the SKILL writes the wordmark + * with explicit spans when it owns the markup. For this default + * rendering, the whole wordmark is ink and the orange comes through + * the bottom border + eyebrow. */ +} +.logo-watermark.top-right::after, +.logo-watermark.bottom-right::after { order: -1; margin-right: 18px; } + +/* ---------------- AI DISCLOSURE BADGE ---------------- */ +/* A small, honest "AI-GENERATED" label so the disclosure rides with the + * card image on social. Bottom-right corner (logo is top-left, citation is + * center-bottom, so this corner is free). Injected into index.html by + * inject.sh. See the /ai policy page. */ +.ai-badge { + position: absolute; + bottom: 74px; /* clears the 6px orange accent bar; sits in the bottom padding band */ + right: 110px; /* matches the card side padding / logo inset */ + z-index: 10; + font-family: var(--font-mono); + font-size: 14pt; + font-weight: 500; + letter-spacing: 0.10em; + text-transform: uppercase; + color: var(--ink-faint); +} + +/* ---------------- Defensive overrides ---------------- */ +/* Anything brand-gen might have set inline using its tokens. */ +* { box-sizing: border-box; } diff --git a/social/linkedin/moc-parity-matrix-reflection/caption.md b/social/linkedin/moc-parity-matrix-reflection/caption.md new file mode 100644 index 00000000..79f4dd5f --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/caption.md @@ -0,0 +1,20 @@ +# Replacing moc with reflection: 29 of 47 matrix rows match, and a class needs no macro + +## Body +I wrote down 47 things moc processes or generates, one row each, and tested every verdict. A "same" row builds the class for real moc next to the reflect-moc version and compares the two meta-objects row for row, then runs both. 29 rows are the same (24 checked against a real moc, 5 by behaviour moc cannot give), 5 differ in a way the note names, and 11 are not supported yet. + +A Qt class, a derived class, a widget class and a gadget now need no macro and no moc input. The base class finds the class it belongs to through a default template argument, so the class is never named twice. + +Each open row has a reason: namespaces, BINDABLE, invokable constructors, plugin metadata and the input of qmltyperegistrar. The observable container from the spec series is one use case among them. + +https://wrocpp.github.io/posts/moc-parity-matrix-reflection/ + +## Hashtags +#cpp #cpp26 #reflection #qt #moc #wrocpp + +## Alt-text +wro.cpp card with the magnet logo. Headline reads "A Qt class with no macro and no moc" with a subtitle about 29 of 47 rows of a moc conformance matrix being the same, 24 checked against a real moc. Citation footer: wro.cpp, 2026-10-29. + +## Suggested post time +Thursday 2026-10-29, 16:00 UTC +Reason: the second slot of the day, the flagship of the Qt series, a few days after the spec post. diff --git a/social/linkedin/moc-parity-matrix-reflection/config.yaml b/social/linkedin/moc-parity-matrix-reflection/config.yaml new file mode 100644 index 00000000..0fd5923b --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/config.yaml @@ -0,0 +1,12 @@ +template: social/linkedin-post + +size: + width: 1200 + height: 1200 + dpr: 2 + +project: + name: "moc-parity-matrix-reflection" + author: "Filip Sajdak, Founder & CEO" + email: "office@scudoai.com" + date: "2026-10-29" diff --git a/social/linkedin/moc-parity-matrix-reflection/content.md b/social/linkedin/moc-parity-matrix-reflection/content.md new file mode 100644 index 00000000..3712b236 --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/content.md @@ -0,0 +1,12 @@ +--- +template: social/linkedin-post +--- + +::::post{type=insight theme=dark logo=top-left} + +:::insight{citation="wro.cpp -- 2026-10-29"} +# A Qt class with no macro and no moc +29 of 47 rows of a moc conformance matrix are the same, each with a test, and 24 are checked against a real moc. The 11 unsupported rows each carry a reason. +::: + +:::: diff --git a/social/linkedin/moc-parity-matrix-reflection/image.png b/social/linkedin/moc-parity-matrix-reflection/image.png new file mode 100644 index 00000000..d4a95c0d Binary files /dev/null and b/social/linkedin/moc-parity-matrix-reflection/image.png differ diff --git a/social/linkedin/moc-parity-matrix-reflection/index.html b/social/linkedin/moc-parity-matrix-reflection/index.html new file mode 100644 index 00000000..1ec36cf6 --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/index.html @@ -0,0 +1,28 @@ + + + + + + moc-parity-matrix-reflection + + + + + + + + + + +

A Qt class with no macro and no moc

29 of 47 rows of a moc conformance matrix are the same, each with a test, and 24 are checked against a real moc. The 11 unsupported rows each carry a reason.

wro.cpp -- 2026-10-29
ScudoAI
AI-generated
+ + \ No newline at end of file diff --git a/social/linkedin/moc-parity-matrix-reflection/media/.gitkeep b/social/linkedin/moc-parity-matrix-reflection/media/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/social/linkedin/moc-parity-matrix-reflection/media/README.md b/social/linkedin/moc-parity-matrix-reflection/media/README.md new file mode 100644 index 00000000..bb81c7fa --- /dev/null +++ b/social/linkedin/moc-parity-matrix-reflection/media/README.md @@ -0,0 +1,9 @@ +# Media directory + +Drop images here (PNG, JPG, SVG) for use in your post. + +Reference them in your content.md with: + + :::image{src="screenshot.png" alt="Alt text" position="top"} + +The renderer resolves paths relative to this media/ directory. diff --git a/social/linkedin/reflection-dangling-signature/caption.md b/social/linkedin/reflection-dangling-signature/caption.md index 29257733..651f1baa 100644 --- a/social/linkedin/reflection-dangling-signature/caption.md +++ b/social/linkedin/reflection-dangling-signature/caption.md @@ -16,5 +16,5 @@ https://wrocpp.github.io/posts/reflection-dangling-signature/ Cream wro.cpp card with the magnet logo. Headline reads "Reflection sees the dangling view GCC 16.2 misses" with a subtitle saying a short lint flags a view returned from a by-value owner and -Wall -Wextra says nothing. Citation footer: wro.cpp, 2026-10-29. ## Suggested post time -Thursday 2026-10-29, 16:00 UTC -Reason: second post of the day; the API contract post holds the 08:00 UTC slot. +Monday 2026-11-02, 16:00 UTC +Reason: the second slot of the day, moved here from 29 October for the Qt flagship. diff --git a/social/linkedin/reflection-dangling-signature/config.yaml b/social/linkedin/reflection-dangling-signature/config.yaml index 8b745209..22293b63 100644 --- a/social/linkedin/reflection-dangling-signature/config.yaml +++ b/social/linkedin/reflection-dangling-signature/config.yaml @@ -9,4 +9,4 @@ project: name: "reflection-dangling-signature" author: "wro.cpp -- Wroclaw C++ community" email: "office@wro.cpp" - date: "2026-10-29" + date: "2026-11-02" diff --git a/social/linkedin/reflection-dangling-signature/content.md b/social/linkedin/reflection-dangling-signature/content.md index 80f30610..71a46880 100644 --- a/social/linkedin/reflection-dangling-signature/content.md +++ b/social/linkedin/reflection-dangling-signature/content.md @@ -4,7 +4,7 @@ template: social/linkedin-post ::::post{type=insight theme=dark logo=top-left} -:::insight{citation="wro.cpp -- 2026-10-29"} +:::insight{citation="wro.cpp -- 2026-11-02"} # Reflection sees the dangling view GCC 16.2 misses A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing. ::: diff --git a/social/linkedin/reflection-dangling-signature/image.png b/social/linkedin/reflection-dangling-signature/image.png index 9d591d40..2e551140 100644 Binary files a/social/linkedin/reflection-dangling-signature/image.png and b/social/linkedin/reflection-dangling-signature/image.png differ diff --git a/social/linkedin/reflection-dangling-signature/index.html b/social/linkedin/reflection-dangling-signature/index.html index adf1257d..80e360ca 100644 --- a/social/linkedin/reflection-dangling-signature/index.html +++ b/social/linkedin/reflection-dangling-signature/index.html @@ -23,6 +23,6 @@ -

Reflection sees the dangling view GCC 16.2 misses

A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing.

wro.cpp -- 2026-10-29
ScudoAI
AI-generated
+

Reflection sees the dangling view GCC 16.2 misses

A short lint flags a view returned from a by-value owner. -Wall -Wextra says nothing.

wro.cpp -- 2026-11-02
ScudoAI
AI-generated
\ No newline at end of file diff --git a/src/content/posts/2026-10-29-moc-parity-matrix-reflection.mdx b/src/content/posts/2026-10-29-moc-parity-matrix-reflection.mdx new file mode 100644 index 00000000..079cc807 --- /dev/null +++ b/src/content/posts/2026-10-29-moc-parity-matrix-reflection.mdx @@ -0,0 +1,138 @@ +--- +title: "Replacing moc with reflection: 29 of 47 matrix rows match, and a class needs no macro" +slug: "moc-parity-matrix-reflection" +pubDate: 2026-10-29 +author: filip-sajdak +language: en +kind: flagship +series: cpp26-reflection +series_order: 41 +audience: working-cpp +tags: [reflection, cpp26, qt, moc, testing, ai] +summary: "I listed 47 things moc processes or generates and gave each a verdict that a test must back. 29 rows are the same: 24 against a real moc's output, 5 by behaviour that moc cannot give. 5 differ in a stated way and 11 are not supported yet. A Qt class and a gadget now need no macro, because the base class finds the class through a default template argument." +draft: false +relatedPosts: [regenerate-qt-containers-from-a-spec, qt-model-edge-cases-catalogue, qt-class-template-typed-signals-without-moc, qt-object-that-never-names-its-class] +--- + +import PostLink from '../../components/PostLink.astro'; + +## In short + +- The question was how much of moc reflection replaces. I wrote 47 rows, one per construct, each with a verdict and a test. +- 29 rows are "same". In 24 of them the meta-object equals a real moc's, and the behaviour matches. The other 5 have no twin. 5 rows are partial, 11 are unsupported, 1 needs a build step and 1 is not needed. +- A QObject class, a derived class, a widget class and a gadget need no macro. The base class names the class for you. +- Every open row has a reason. The largest: namespaces, `BINDABLE`, invokable constructors, plugin metadata and the `qmltyperegistrar` input. +- The container from the spec series is one use case. A class template with signals is a row moc cannot have. + +## How a row is proved + +A row names a moc construct, its spelling in moc and in reflect-moc, a verdict and a test. A script run by ctest fails on a blank cell, an unknown verdict or a missing test file. It also fails on a "same" row without a test. + +In 24 of the 29 "same" rows there is a twin. One executable holds the class written for real moc in one header and for reflect-moc in another. The test compares the two meta-objects row for row: names, signatures, parameter names, access, flags, revisions, properties, enums and class info. Then it runs both. It calls methods by name, round-trips properties, connects signals and casts with `qobject_cast`. + +The other 5 are class templates, the class-name override, `qobject_cast` and two compile-time errors. I broke a twin on purpose once, by adding a property to the moc side only. The comparison failed. Without that check a comparison that always passes would look the same as one that works. + +| verdict | rows | meaning | +|---|---|---| +| same | 29 | 24 equal real moc's meta-object and behaviour; 5 are tested without a twin | +| partial | 5 | works, with a difference the note names | +| unsupported | 11 | does not build, or is ignored with a message | +| impossible | 1 | needs a build step that reads moc's JSON | +| not needed | 1 | there is no generated file to include | + +## A class with no macro + +This is a test, with its comments cut. It uses no `Q_` or `RQT_` macro and no moc input. + +```cpp +class Counter : public rqt::ObjectOf<> { + public: + using ObjectOf::ObjectOf; + + [[= rqt::property{.write = "setValue", .notify = "valueChanged"}]] int value() const { return value_; } + [[= rqt::slot]] void setValue(int v) { + if (v == value_) return; + value_ = v; + valueChanged(v); + } + [[= rqt::invokable]] int doubled() const { return 2 * value_; } + + rqt::signal valueChanged; + [[= rqt::signal_function]] void finished() { rqt::activate{this}(); } + + private: + int value_ = kStart; +}; +``` + +The base class supplies what `Q_OBJECT` declares. That is the meta-object function, the cast and call functions, `tr` and the static meta-object that `qobject_cast` and pointer-to-member connect read. It also gives the data-member signals their owner. Stock Qt sees an ordinary class. + +The base adds no data member. I measured it on aarch64 with GCC 16.2: an empty class is 16 bytes, the size of `QObject`. With `RQT_OBJECT` it is 24, because that macro adds a hidden member. With two signals the sizes are 32 and 40 bytes. + +A derived class names its base the same way. The form `rqt::ObjectOf` extends `Counter`. The form `rqt::ObjectOf` extends a widget. Both are tested, with a class in a namespace, a nested class and a class template. + +## How the class names itself + +The base needs the derived class, because the meta-object, the casts and the owner of a signal belong to it. The usual way is to write the class twice: `class Counter : public Base`. The second `Counter` is what I wanted to remove. + +The trick is a default template argument. The base takes the class as a second parameter, and its default is `std::meta::current_class()`. A default argument is evaluated where the template is named. In a base clause that is the class being defined. + +Four other ways, tried on GCC 16.2 in small files, except the first, which the language rules out. + +| idea | result | +|---|---| +| deducing this for the overrides | cannot work: a virtual function takes no explicit object parameter | +| `current_class()` in a default member initializer | evaluated where the object is made, not in the class | +| `current_class()` in a static member initializer | works, but needs a line in every class | +| the parent of a lambda type in the base clause | works, and is longer to write | + +A class template needed one more step. The default must depend on a template parameter, or GCC evaluates it once, where the template is defined. With that, `Box` gets its own meta-object, with the class name `Box`. A plain alias template in front of the base fails for the same reason, as I checked in a small file. The alias has to repeat the default argument. + +## The closed rows + +| construct | spelling in reflect-moc | test | +|---|---|---| +| `Q_OBJECT` class | `ObjectOf<>` | `parity_macro_free_class` | +| `Q_GADGET` | `Gadget<>`, `Gadget` for a derived one | `parity_gadget` | +| `Q_REVISION`, `Q_SCRIPTABLE` on methods | `[[=rqt::revision{1, 2}]]`, `[[=rqt::scriptable]]` | `parity_method_flags` | +| property `REVISION` | `.revision = {1, 2}` | `parity_revision` | +| `QPrivateSignal` | the tag as the last parameter | `parity_private_signal` | +| `Q_ENUM`, `Q_FLAG` | `[[=rqt::enum_]]`, `[[=rqt::flag]]` | `parity_enum_from_type` | +| overloaded signals, slots, invokables | one annotation per function | `parity_overloads_function_form` | + +A gadget has no `QObject`, so Qt hands its address over as a pointer to a `QObject`. Its property code runs in the static meta-call, as moc's gadgets do. Reading and writing properties on a gadget, invoking its methods and storing it in a `QVariant` work with stock Qt. + +One more rule came out of this work. What the parser accepts, the table builder must not ignore. A property revision used to be read and dropped without a word. Now an annotation on the wrong kind of member stops the build, with a test for each case. + +## The container as one use case + +The container that started this series needs a class template with typed signals. moc cannot read a class template. So that row has no twin. It is one of the 5 "same" rows tested without one, and it goes beyond moc. + +The regenerated spec runs on it. Its author's two corrections are fixed in both bindings. The last post has the numbers. + +## What is still open, and why + +| rows | construct | reason | +|---|---|---| +| namespaces | `Q_NAMESPACE`, `Q_ENUM_NS` | not tried. A namespace is never complete, but reflection can list its members | +| constructors | `Q_INVOKABLE` on a constructor | not done. Annotated constructors can be reflected | +| bindable | `BINDABLE` | stops the build with a message, tested. Resolving the getter is not done | +| private | `Q_PRIVATE_SLOT`, `Q_PRIVATE_PROPERTY` | the d-pointer class is usually incomplete in the header | +| plugin | `Q_PLUGIN_METADATA` | the JSON must be read at compile time and turned into CBOR by constexpr code | +| QML | `QML_ELEMENT` and its family | registered by a call, not by the class: partial | +| JSON | the input of `qmltyperegistrar` | a build step. A program built from the same classes could print it | +| overloads | one signal member with several signatures; an overloaded setter | two data members cannot share a name; a setter binds to the first overload | +| enums | `QMetaEnum::fromType` in a macro-free class | needs two friend functions that a base class cannot declare for a derived class | + +The last row I checked by compiling a small file: the static assertion in Qt fails. The meta-object lists the enum, and a test covers that. There is no test yet for the failing `fromType` call. + +## What this does not show + +- Only GCC 16.2 builds this, because only it has reflection here. Binding A of the spec also builds with MSVC and Clang, but that code uses moc. +- Qt 6.10.3 only. `lupdate` scans source text and was not tried against these classes. +- AI agents wrote most of the tests and the twins, under my direction. I reviewed the matrix and every verdict, not every line. +- 29 of 47 counts rows, and the rows are not equal in weight. A property row weighs more than a class-info row. + +## Where to read it + +The matrix is [`docs/moc-parity.md`](https://github.com/wrocpp/reflect-moc/blob/b2534166aa858f1b5acde22b7c1cc087d27ec4e3/docs/moc-parity.md), and the tests are in `tests/qt/parity` beside it. The macro audit, a table of each macro and its replacement, is at the end of the matrix. diff --git a/src/content/posts/2026-10-29-reflection-dangling-signature.mdx b/src/content/posts/2026-11-02-reflection-dangling-signature.mdx similarity index 99% rename from src/content/posts/2026-10-29-reflection-dangling-signature.mdx rename to src/content/posts/2026-11-02-reflection-dangling-signature.mdx index 266693c1..bbef5216 100644 --- a/src/content/posts/2026-10-29-reflection-dangling-signature.mdx +++ b/src/content/posts/2026-11-02-reflection-dangling-signature.mdx @@ -1,7 +1,7 @@ --- title: "Reflection can flag a dangling signature that GCC 16.2 does not warn about" slug: "reflection-dangling-signature" -pubDate: 2026-10-29 +pubDate: 2026-11-02 author: filip-sajdak language: en tags: [news, cpp26, reflection, safety, lifetime, gcc]