6/02/2025

C++ Variadic Template example code for studying

 

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:

  1. ... in template: Declares a parameter pack that can accept any number of arguments
  2. ... after variable: Expands the parameter pack
  3. sizeof...(): Gets the number of elements in a pack
  4. Sequence: A compile-time container of values (usually integers)
  5. In CK: Sequences are used to specify which dimensions to transform and how to map them

No comments:

Post a Comment