12/15/2025

C++20, consteval and comma operator example code

 

.

// (study.marearts.com): Compile-time Validation Pattern with consteval + static_assert
// C++20 required for consteval

#include <iostream>

// ==========================================
// EXAMPLE 1: Simple consteval function
// ==========================================

// consteval = MUST run at compile time (C++20 feature)
consteval int add(int a, int b) {
return a + b;
}

void example1() {
// This is evaluated at COMPILE TIME!
constexpr int result = add(5, 3); // Compiler computes: 5 + 3 = 8
std::cout << "(study.marearts.com) Example 1 : 5 + 3 = " << result << std::endl;
}

// ==========================================
// EXAMPLE 2: Comma operator
// ==========================================

void printMessage() {
std::cout << "Message printed!" << std::endl;
// Returns nothing (void)
}

void example2() {
// Comma operator: (A, B) evaluates A, then returns B
int x = (printMessage(), 42);
// ↓ ↓
// Calls function Returns 42
std::cout << "(study.marearts.com) Example 2: x = " << x << std::endl; // x = 42
}

// ==========================================
// EXAMPLE 3: consteval with static_assert (VALIDATION!)
// ==========================================

// Validation function - checks if number is positive
template <int N>
consteval void ValidatePositive() {
static_assert(N > 0, "ERROR: Number must be positive!");
// If N <= 0, compilation FAILS here with error message
// If N > 0, function completes
}

// Example struct that validates its template parameter
template <int Number>
struct PositiveNumber {
// This line forces validation at compile time!
// If Number is negative, compilation fails
static constexpr auto kValidation = (ValidatePositive<Number>(), 0);
// ↓ ↓
// Run checks (compile time) Return 0
int value = Number;
void print() {
std::cout << "Positive number: " << value << std::endl;
}
};

void example3() {
std::cout << "\n(study.marearts.com) Example 3: Compile-time validation" << std::endl;
// ✅ This COMPILES (5 > 0)
PositiveNumber<5> good;
good.print();
// ❌ This would FAIL to compile (uncomment to see error!)
// PositiveNumber<-3> bad;
// Error: static assertion failed: ERROR: Number must be positive!
std::cout << "✓ Validation passed at compile time!" << std::endl;
}

// ==========================================
// EXAMPLE 4: Real-world pattern (like our code!)
// ==========================================

enum class OperationType {
PASS_THROUGH,
ADD_ONE,
MULTIPLY_TWO
};

// Configuration struct (like our ConvSignature)
template <OperationType Op>
struct Config {
static constexpr OperationType operation = Op;
};

// Validation: Only allow PASS_THROUGH
template <auto Cfg>
consteval void ValidateConfig() {
static_assert(Cfg.operation == OperationType::PASS_THROUGH,
"ERROR: Simple implementation only supports PASS_THROUGH!");
}

// Simple implementation (like our ReferenceForwardFactory)
template <auto CONFIG>
struct SimpleProcessor {
// Validate at compile time!
static constexpr auto kValidation = (ValidateConfig<CONFIG>(), 0);
void process(int x) {
std::cout << "Processing " << x << " (PassThrough only)" << std::endl;
}
};

void example4() {
std::cout << "\nExample 4: Real-world validation pattern" << std::endl;
// ✅ This COMPILES (uses PASS_THROUGH)
constexpr Config<OperationType::PASS_THROUGH> good_config;
SimpleProcessor<good_config> processor;
processor.process(42);
// ❌ This would FAIL to compile (uncomment to see error!)
// constexpr Config<OperationType::ADD_ONE> bad_config;
// SimpleProcessor<bad_config> bad_processor;
// Error: static assertion failed: Simple implementation only supports PASS_THROUGH!
std::cout << "✓ Config validation passed!" << std::endl;
}

// ==========================================
// MAIN
// ==========================================

int main() {
std::cout << "=== Compile-Time Validation Study ===" << std::endl;
example1(); // consteval basics
example2(); // comma operator
example3(); // consteval + static_assert
example4(); // real-world pattern
std::cout << "\n✓ All examples passed!" << std::endl;
return 0;
}

..

KEY CONCEPTS:

1. consteval (C++20):
- Function MUST execute at compile time
- Cannot be called at runtime

2. static_assert (C++11):
- Checks condition at compile time
- If false, compilation FAILS with error message

3. Comma operator:
- (A, B) executes A, then returns B
- Used to "call void function" in member initialization

4. The Pattern:
static constexpr auto kValidation = (ValidateFunction<>(), 0);
- Forces compile-time validation
- Returns 0 if validation passes
- Compilation fails if validation fails

TO COMPILE:
g++ -std=c++20 compile_time_validation.cpp -o compile_time_validation
./compile_time_validation


=== Compile-Time Validation Study ===
Example 1: 5 + 3 = 8
Message printed!
Example 2: x = 42

Example 3: Compile-time validation
Positive number: 5
✓ Validation passed at compile time!

Example 4: Real-world validation pattern
Processing 42 (PassThrough only)
✓ Config validation passed!

✓ All examples passed!


Summary: C++ Version Requirements

Our pattern needs: C++20 (for consteval)


Quick Reference

✅ Good Case (Passes):

❌ Bad Case (Fails):

To Experiment

Try uncommenting line 75 in the study file:
Then compile:
You'll see the compile error!




12/12/2025

Why Can't We Use Functions in "requires"? #04_why_not_both.cpp

 

.

// Example 4: Why Can't We Use Functions in "requires"?
// This shows the difference between concepts and constexpr functions

#include <iostream>
#include <concepts>

// ==========================================
// Define types
// ==========================================

struct Dog {
const char* name = "MareArts.com";
};

struct Cat {
const char* name = "Whiskers";
};

// ==========================================
// METHOD 1: Using CONCEPT (works in requires)
// ==========================================

template <typename T>
concept HasName = requires(T t) {
{ t.name } -> std::convertible_to<const char*>;
};

template <typename T>
requires HasName<T> // ✅ Works! Concept in requires clause
void printNameWithConcept(T animal) {
std::cout << "Name: " << animal.name << "\n";
}

// ==========================================
// METHOD 2: Using CONSTEXPR FUNCTION (doesn't work in requires!)
// ==========================================

template <typename T>
consteval bool hasNameFunction() {
return requires(T t) {
{ t.name } -> std::convertible_to<const char*>;
};
}

// ❌ This won't compile!
// template <typename T>
// requires hasNameFunction<T>() // ❌ ERROR: Can't use function in requires!
// void printNameWithFunction(T animal) {
// std::cout << "Name: " << animal.name << "\n";
// }

// ==========================================
// METHOD 3: Using CONSTEXPR FUNCTION with "if constexpr" (works!)
// ==========================================

template <typename T>
void printNameIfConstexpr(T animal) {
// ✅ Works! Function in "if constexpr"
if constexpr (hasNameFunction<T>()) {
std::cout << "Name: " << animal.name << "\n";
}
else {
std::cout << "No name!\n";
}
}

// ==========================================
// WHY THE DIFFERENCE?
// ==========================================

/*
"requires" clause:
- Part of template declaration
- Needs a CONCEPT (compile-time constraint)
- Determines if template can even exist
- Syntax: template <...> requires CONCEPT<T>

"if constexpr":
- Inside function body
- Can use ANY compile-time expression
- Chooses which code branch to keep
- Syntax: if constexpr (EXPRESSION)
*/

// ==========================================
// Main
// ==========================================

int main() {
Dog dog;
Cat cat;
std::cout << "=== Using Concept in 'requires' ===\n";
printNameWithConcept(dog);
printNameWithConcept(cat);
std::cout << "\n=== Using Function in 'if constexpr' ===\n";
printNameIfConstexpr(dog);
printNameIfConstexpr(cat);
return 0;
}

/*
SUMMARY - Where Can You Use What?

┌──────────────────────────┬──────────────┬──────────────────┐
│ │ "requires" │ "if constexpr" │
├──────────────────────────┼──────────────┼──────────────────┤
│ Concept │ ✅ │ ✅ │
│ Constexpr Function │ ❌ │ ✅ │
│ Any compile-time expr │ ❌ │ ✅ │
└──────────────────────────┴──────────────┴──────────────────┘

This is why:
- Factories use concepts in "requires" (IsReferenceAlgorithm concept)
- Dispatcher uses constexpr functions in "if constexpr" (IsReferenceAlgorithm())

After refactoring:
- We removed the concept (not needed anymore)
- Kept the constexpr function (used in dispatcher)
- Removed algorithm checks from factory "requires" clauses
*/


..


.

### 4. Why Not Both? (04_why_not_both.cpp)
**What it teaches:** Why we can't mix them up

```
┌──────────────────────────┬──────────────┬──────────────────┐
│ │ "requires" │ "if constexpr" │
├──────────────────────────┼──────────────┼──────────────────┤
│ Concept │ ✅ │ ✅ │
│ Constexpr Function │ ❌ │ ✅ │
└──────────────────────────┴──────────────┴──────────────────┘
```

**Key Point:** Constexpr functions DON'T work in `requires` clauses!

..

Now #1~#4 series is done.

g++ -std=c++20 01_concept_basics.cpp -o 01_concepts
g++ -std=c++20 02_constexpr_functions.cpp -o 02_constexpr
g++ -std=c++20 03_dispatcher_pattern.cpp -o 03_dispatcher
g++ -std=c++20 04_why_not_both.cpp -o 04_why


recap!

## Key Takeaways 🎯

1. **Concepts** check if types have certain features
- Used in `requires` clauses
- Like: `requires HasName<T>`

2. **Constexpr functions** run at compile time
- Used in `if constexpr` statements
- Like: `if constexpr (isDog<T>())`

3. **Dispatcher pattern** separates concerns:
- **Dispatcher** checks algorithm type (using `if constexpr` + constexpr functions)
- **Factory** checks direction only (using `requires` + concepts)

4. **You can't mix them:**
- Constexpr functions DON'T work in `requires` clauses
- That's why we removed the algorithm check from factory `requires` clauses!


Thank you!

study.marearts.com


Dispatcher Pattern Example #03_dispatcher_pattern.cpp

 

.

// Example 3: The Dispatcher Pattern (Like CK Convolution!)
// This is exactly how the convolution dispatcher works!

#include <iostream>
#include <concepts>

// ==========================================
// Step 1: Define algorithm types
// ==========================================

enum class AlgorithmType {
SIMPLE,
FAST,
PARALLEL
};

// Simple algorithm
struct SimpleAlgorithm {
static constexpr AlgorithmType type = AlgorithmType::SIMPLE;
};

// Fast algorithm
struct FastAlgorithm {
static constexpr AlgorithmType type = AlgorithmType::FAST;
int optimization_level = 3;
};

// Parallel algorithm
struct ParallelAlgorithm {
static constexpr AlgorithmType type = AlgorithmType::PARALLEL;
int num_threads = 8;
};

// ==========================================
// Step 2: Algorithm detection functions (like IsReferenceAlgorithm!)
// ==========================================

// Check if algorithm is Simple
template <typename T>
consteval bool IsSimpleAlgorithm() {
return std::is_same_v<T, SimpleAlgorithm>;
}

// Check if algorithm is Fast
template <typename T>
consteval bool IsFastAlgorithm() {
return std::is_same_v<T, FastAlgorithm>;
}

// Check if algorithm is Parallel
template <typename T>
consteval bool IsParallelAlgorithm() {
return std::is_same_v<T, ParallelAlgorithm>;
}

// ==========================================
// Step 3: Factories (only check direction, not algorithm!)
// ==========================================

enum class Direction {
FORWARD,
BACKWARD
};

// Factory for Simple Algorithm
template <Direction DIR, typename Algorithm>
requires (DIR == Direction::FORWARD) // ← Only check direction!
struct SimpleFactory {
static void process() {
std::cout << "SimpleFactory: Processing with basic algorithm\n";
}
};

// Factory for Fast Algorithm
template <Direction DIR, typename Algorithm>
requires (DIR == Direction::FORWARD) // ← Only check direction!
struct FastFactory {
static void process() {
std::cout << "FastFactory: Processing with optimized algorithm\n";
}
};

// Factory for Parallel Algorithm
template <Direction DIR, typename Algorithm>
requires (DIR == Direction::FORWARD) // ← Only check direction!
struct ParallelFactory {
static void process() {
std::cout << "ParallelFactory: Processing with parallel algorithm\n";
}
};

// ==========================================
// Step 4: DISPATCHER - Routes to correct factory
// ==========================================

template <Direction DIR, typename Algorithm>
void dispatch() {
std::cout << "\n=== DISPATCHER START (MareArts.com)===\n";
// Check direction first
if constexpr (DIR == Direction::FORWARD) {
std::cout << "Direction: FORWARD\n";
// Check algorithm type (like the conv dispatcher!)
if constexpr (IsSimpleAlgorithm<Algorithm>()) { // ← Check algorithm here!
std::cout << "Algorithm: SIMPLE\n";
SimpleFactory<DIR, Algorithm>::process();
}
else if constexpr (IsFastAlgorithm<Algorithm>()) { // ← Check algorithm here!
std::cout << "Algorithm: FAST\n";
FastFactory<DIR, Algorithm>::process();
}
else if constexpr (IsParallelAlgorithm<Algorithm>()) { // ← Check algorithm here!
std::cout << "Algorithm: PARALLEL\n";
ParallelFactory<DIR, Algorithm>::process();
}
else {
// Force compile error for unknown algorithms
[]<bool flag = false>() { static_assert(flag, "Unknown algorithm!"); }();
}
}
else {
std::cout << "Direction: BACKWARD\n";
std::cout << "Not implemented yet!\n";
}
std::cout << "=== DISPATCHER END ===\n\n";
}

// ==========================================
// Main - Try different algorithms!
// ==========================================

int main() {
std::cout << "=== Dispatcher Pattern Example ===\n";
std::cout << "This is exactly how CK convolution dispatcher works!\n";
// Try Simple algorithm
dispatch<Direction::FORWARD, SimpleAlgorithm>();
// Try Fast algorithm
dispatch<Direction::FORWARD, FastAlgorithm>();
// Try Parallel algorithm
dispatch<Direction::FORWARD, ParallelAlgorithm>();
return 0;
}

/*
DISPATCHER PATTERN SUMMARY:

┌─────────────────────────────────────────┐
│ USER CODE │
│ dispatch<FORWARD, SimpleAlgorithm>() │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ DISPATCHER │
│ ✅ Check direction (if constexpr) │
│ ✅ Check algorithm (if constexpr) │
│ - IsSimpleAlgorithm() │
│ - IsFastAlgorithm() │
│ - IsParallelAlgorithm() │
│ ✅ Route to correct factory │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ FACTORY │
│ requires (DIR == FORWARD) │
│ ❌ NO algorithm check! │
│ ✅ Just build the thing │
└─────────────────────────────────────────┘

KEY POINTS:
1. Dispatcher uses consteval functions with "if constexpr"
2. Factories use concepts with "requires"
3. Algorithm checking happens in dispatcher, not factory
4. This is EXACTLY the CK convolution pattern!
*/


..

### 3. Dispatcher Pattern (03_dispatcher_pattern.cpp) ⭐ MOST IMPORTANT
**What it teaches:** Exactly how CK convolution works!

```cpp
// Detection functions (like IsReferenceAlgorithm!)
template <typename T>
consteval bool IsSimpleAlgorithm() {
return std::is_same_v<T, SimpleAlgorithm>;
}

// Dispatcher - checks algorithm and routes
template <Direction DIR, typename Algorithm>
void dispatch() {
if constexpr (DIR == Direction::FORWARD) {
// Check algorithm type here!
if constexpr (IsSimpleAlgorithm<Algorithm>()) {
SimpleFactory<DIR, Algorithm>::process();
}
else if constexpr (IsFastAlgorithm<Algorithm>()) {
FastFactory<DIR, Algorithm>::process();
}
}
}

// Factory - only checks direction!
template <Direction DIR, typename Algorithm>
requires (DIR == Direction::FORWARD) // Only check direction
struct SimpleFactory {
// NO algorithm check here!
};
```

**Output:**
```
=== DISPATCHER START ===
Direction: FORWARD
Algorithm: SIMPLE
SimpleFactory: Processing with basic algorithm
=== DISPATCHER END ===
```

**Key Point:** This is EXACTLY the CK pattern!


Study.marearts.com!!!