Example 1: Basic Variadic Template
#include <iostream>
// 1. Simple function that accepts any number of arguments
template <typename... Args>
void print_simple(Args... args) {
std::cout << "Number of arguments: " << sizeof...(args) << std::endl;
}
int main() {
print_simple(); // 0 arguments
print_simple(1); // 1 argument
print_simple(1, 2.5, "hello"); // 3 arguments
return 0;
}
Example 2: Print All Arguments (Recursion)
#include <iostream>
// Base case: no arguments left
void print() {
std::cout << std::endl;
}
// Recursive case: print first, then rest
template <typename First, typename... Rest>
void print(First first, Rest... rest) {
std::cout << first << " ";
print(rest...); // Call with remaining arguments
}
int main() {
print(1, 2, 3); // Output: 1 2 3
print("Hello", 42, 3.14, 'A'); // Output: Hello 42 3.14 A
return 0;
}
Example 3: Simple Integer Sequence
#include <iostream>
// Our own simple Sequence class
template <int... Values>
struct Sequence {
static constexpr int size = sizeof...(Values);
static void print() {
std::cout << "Sequence contains " << size << " values: ";
((std::cout << Values << " "), ...); // C++17 fold expression
std::cout << std::endl;
}
};
int main() {
using Seq1 = Sequence<1, 2, 3>;
using Seq2 = Sequence<10>;
using Seq3 = Sequence<>; // empty
Seq1::print(); // Output: Sequence contains 3 values: 1 2 3
Seq2::print(); // Output: Sequence contains 1 values: 10
Seq3::print(); // Output: Sequence contains 0 values:
std::cout << "Seq1 size: " << Seq1::size << std::endl; // Output: 3
return 0;
}
Example 4: Sequence with Array Access
#include <iostream>
#include <array>
template <int... Values>
struct Sequence {
static constexpr int size = sizeof...(Values);
static constexpr std::array<int, size> values = {Values...};
static constexpr int at(int index) {
return values[index];
}
static void print_all() {
for (int i = 0; i < size; ++i) {
std::cout << "Index " << i << ": " << at(i) << std::endl;
}
}
};
int main() {
using MySeq = Sequence<10, 20, 30, 40>;
std::cout << "Size: " << MySeq::size << std::endl;
std::cout << "Element at index 2: " << MySeq::at(2) << std::endl;
MySeq::print_all();
return 0;
}
Example 5: Type Sequence (Like std::tuple)
#include <iostream>
#include <typeinfo>
// Store multiple types
template <typename... Types>
struct TypeList {
static constexpr int size = sizeof...(Types);
};
// Helper to print type names
template <typename T>
void print_type() {
std::cout << typeid(T).name() << " ";
}
template <typename... Types>
void print_types() {
std::cout << "Types (" << sizeof...(Types) << "): ";
((print_type<Types>()), ...);
std::cout << std::endl;
}
int main() {
using MyTypes = TypeList<int, double, char>;
std::cout << "Number of types: " << MyTypes::size << std::endl;
print_types<int, double, char>();
print_types<std::string>();
return 0;
}
Example 6: CK-Style Sequence Usage
#include <iostream>
// Simplified CK-style Sequence
template <int... Is>
struct Sequence {
static constexpr int Size() { return sizeof...(Is); }
template <int N>
static constexpr int At() {
constexpr int arr[] = {Is...};
return arr[N];
}
};
// Alias for convenience
template <int... Is>
using S = Sequence<Is...>;
// Function that uses sequences for dimension mapping
template <int... InputDims, int... OutputDims>
void describe_mapping(S<InputDims...>, S<OutputDims...>) {
std::cout << "Mapping: ";
std::cout << "Input dims [";
((std::cout << InputDims << " "), ...);
std::cout << "] -> Output dims [";
((std::cout << OutputDims << " "), ...);
std::cout << "]" << std::endl;
}
int main() {
// Define dimension indices
S<0> batch_dim{}; // Dimension 0: batch
S<1> height_dim{}; // Dimension 1: height
S<2> width_dim{}; // Dimension 2: width
S<3> channel_dim{}; // Dimension 3: channel
// Example: NHWC to NCHW transformation mapping
describe_mapping(S<0,1,2,3>{}, S<0,3,1,2>{});
// Output: Mapping: Input dims [0 1 2 3 ] -> Output dims [0 3 1 2 ]
// Example: Merge height and width
describe_mapping(S<0,1,2,3>{}, S<0,1,2>{});
// Output: Mapping: Input dims [0 1 2 3 ] -> Output dims [0 1 2 ]
// Access sequence properties
using NHWC = S<0,1,2,3>;
std::cout << "NHWC dimensions: " << NHWC::Size() << std::endl;
std::cout << "Height dimension index: " << NHWC::At<1>() << std::endl;
return 0;
}
Example 7: Advanced - Tensor Transformation Simulator
#include <iostream>
#include <vector>
template <int... Dims>
struct Sequence {
static constexpr int size = sizeof...(Dims);
static constexpr int dims[size] = {Dims...};
};
template <int... Is>
using S = Sequence<Is...>;
// Simplified tensor descriptor
struct TensorDesc {
std::vector<int> shape;
std::vector<std::string> dim_names;
TensorDesc(std::initializer_list<int> s, std::initializer_list<std::string> n)
: shape(s), dim_names(n) {}
void print() const {
std::cout << "Tensor shape: ";
for (size_t i = 0; i < shape.size(); ++i) {
std::cout << dim_names[i] << "=" << shape[i] << " ";
}
std::cout << std::endl;
}
};
// Transform function that uses sequences
template <int... InputIndices, int... OutputIndices>
TensorDesc transform_tensor(const TensorDesc& input,
S<InputIndices...> in_mapping,
S<OutputIndices...> out_mapping) {
std::vector<int> new_shape;
std::vector<std::string> new_names;
// Simple pass-through transformation
constexpr int in_dims[] = {InputIndices...};
for (int idx : in_dims) {
new_shape.push_back(input.shape[idx]);
new_names.push_back(input.dim_names[idx]);
}
return TensorDesc{new_shape, new_names};
}
int main() {
// Create NHWC tensor
TensorDesc input_tensor({32, 224, 224, 3}, {"N", "H", "W", "C"});
std::cout << "Input ";
input_tensor.print();
// Transform to NCH (drop W dimension)
auto output = transform_tensor(input_tensor,
S<0,3,1>{}, // Take N, C, H
S<0,1,2>{}); // Map to positions 0,1,2
std::cout << "Output ";
output.print();
// Another example: reorder to NCHW
auto reordered = transform_tensor(input_tensor,
S<0,3,1,2>{}, // N,C,H,W order
S<0,1,2,3>{}); // Same positions
std::cout << "Reordered ";
reordered.print();
return 0;
}
Key Concepts to Remember:
...
in template: Declares a parameter pack that can accept any number of arguments...
after variable: Expands the parameter packsizeof...()
: Gets the number of elements in a pack- Sequence: A compile-time container of values (usually integers)
- In CK: Sequences are used to specify which dimensions to transform and how to map them