# Template Alchemy: Mastering Variadic Packs with TypePack

In the previous chapters, we treated our `TypePack` primarily as a linear sequence—a list that can be sliced, grown, and flattened. However, in many metaprogramming scenarios, a parameter pack acts more like a **Set**.

You might need to know *if* a specific interface exists in a collection, find the specific index of a type to access a corresponding value in a `std::tuple`, or strip away redundant duplicates to create a canonical list of types.

In this article, we will implement three crucial set-operations: `contains` (existence), `index_of` (lookup), and `unique` (deduplication).

### 1\. Existence: The `contains` Check

Checking if a type exists within a pack used to require complex recursive inheritance or `std::disjunction`. With C++17, this becomes a trivial one-liner using **Fold Expressions**.

We add a static constexpr boolean to our main structure. This allows users to write `Pack::contains<int>` effectively.

```cpp
template <class... Ts>
struct TypePack : std::type_identity<TypePack<Ts...>> {
    // ... previous code ...

    // C++17 Fold Expression to check for type existence
    template <class T>
    static constexpr bool contains = (std::is_same_v<T, Ts> || ...);
    
    // ...
};
```

### 2\. Location: The `index_of` Operation

Knowing a type exists is often only half the battle. If you are building a storage system (like a Variant or a Tuple wrapper), you often need the numerical index of that type to access memory.

Since parameter packs cannot be indexed at runtime, we perform a compile-time linear search. We recurse through the pack:

1. **Found:** Return 0.
    
2. **Not Found:** Return the result of the recursion + 1.
    
3. **End of Pack:** Return a sentinel value (max `size_t`) to indicate failure.
    

We add this as a static member to `TypePack`:

```cpp
template <class... Ts>
struct TypePack : std::type_identity<TypePack<Ts...>> {
    // ...
    template <class T>
    static constexpr size_t index_of = details::TypePackIndexOf<T, TypePack>::value;
};
```

And the implementation logic:

```cpp
template <class T, class Pack>
struct TypePackIndexOf;

// Base Case: Pack is empty, type not found. Return MAX_SIZE_T.
template <class T>
struct TypePackIndexOf<T, TypePack<>> : 
    std::integral_constant<size_t, std::numeric_limits<size_t>::max()> {};

// Match Case: The head of the pack matches T. Index is 0.
template <class T, class... Ts>
struct TypePackIndexOf<T, TypePack<T, Ts...>> : 
    std::integral_constant<size_t, 0> {};

// Recursive Step: Head does not match. Check the tail.
template <class T, class TFirst, class... Ts>
struct TypePackIndexOf<T, TypePack<TFirst, Ts...>> :
    std::integral_constant<size_t,
        TypePackIndexOf<T, TypePack<Ts...>>::value == std::numeric_limits<size_t>::max() 
            ? std::numeric_limits<size_t>::max() 
            : TypePackIndexOf<T, TypePack<Ts...>>::value + 1
    > {};
```

### 3\. Uniqueness: The `unique` Transformation

When automatically generating types (e.g., deducing return types from a set of functions), you often end up with duplicates. To resolve ambiguity, you need to reduce the pack to a set of unique types.

This implementation relies on the `TypePackRemove` utility we built in **Part 6**. The strategy is "Filter and Conserve":

1. Take the first type `T`.
    
2. **Remove all occurrences** of `T` from the *rest* of the pack.
    
3. Recurse on the now-filtered tail.
    
4. Prepend `T` back to the result.
    

This ensures order is preserved while duplicates are eliminated.

```cpp
// Public Alias
template <IsSpecializationOf<TypePack> Pack>
using type_pack_unique_t = details::TypePackUnique<Pack>::type;

// Implementation
template <class Pack>
struct TypePackUnique;

// Base Case: Empty pack is already unique
template <>
struct TypePackUnique<TypePack<>> : std::type_identity<TypePack<>> {};

// Recursive Step
template <class T, class... Ts>
struct TypePackUnique<TypePack<T, Ts...>> :
    TypePackInsertAtFirstPosition<
        T, 
        // 1. Remove T from the tail (Ts...)
        // 2. Compute Unique on that filtered tail
        typename TypePackUnique<
            typename TypePackRemove<T, TypePack<Ts...>>::type
        >::type
    > {};
```

### Validation

We verify our set operations with a suite of tests covering existence, valid/invalid indices, and duplicate removal.

C++

```cpp
// 1. Testing Contains
TEST(TypePackTests, Contains)
{
    using Pack = TypePack<int, long, double, char>;
    static_assert(Pack::contains<int>);
    static_assert(Pack::contains<double>);
    static_assert(!Pack::contains<char8_t>); // Not in pack
    
    // Edge case: Empty pack
    static_assert(!TypePack<>::contains<int>);
}

// 2. Testing IndexOf
TEST(TypePackTests, IndexOf)
{
    using Pack = TypePack<int, long, double, char>;
    
    static_assert(Pack::index_of<int> == 0);
    static_assert(Pack::index_of<double> == 2);
    static_assert(Pack::index_of<char> == 3);
    
    // Not found returns MAX
    static_assert(Pack::index_of<char8_t> == std::numeric_limits<size_t>::max());
    static_assert(TypePack<>::index_of<int> == std::numeric_limits<size_t>::max());
}

// 3. Testing Unique
TEST(TypePackTests, Unique)
{
    // Input contains duplicates of int, long, and char
    using Pack = TypePack<int, long, int, char, long, long, char, char8_t>;
    using Expected = TypePack<int, long, char, char8_t>;
    
    static_assert(std::is_same_v<type_pack_unique_t<Pack>, Expected>);

    // Already unique packs remain unchanged
    static_assert(std::is_same_v<type_pack_unique_t<TypePack<int>>, TypePack<int>>);
    static_assert(std::is_same_v<type_pack_unique_t<TypePack<>>, TypePack<>>);
}
```

### Conclusion

With the addition of `contains`, `index_of`, and `unique`, our `TypePack` has evolved into a fully functional compile-time container. We can now treat variadic templates not just as a list of arguments, but as a searchable, indexable, and distinct set of types.

You can explore the full source code for the library on GitHub, including the [implementation details](https://github.com/antrv/my-programming-language/blob/main/skarnc/src/TypePack.h) and the [comprehensive test suite](https://github.com/antrv/my-programming-language/blob/main/skarnc/test/TypePackTests.cpp).

This concludes our deep dive into the core implementation of `TypePack`. By combining structural manipulation (Concat, Slice) with introspective logic (Find, Unique), we have built a library capable of handling complex C++ metaprogramming tasks with clean, readable syntax.
