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
No comments:
Post a Comment