Skip to content

refactor: Consolidate adapter utilities (typed_adapter, smart_adapter) into single generic adapter #299

Description

@kcenon

Summary

Consolidate the multiple adapter utility classes (typed_adapter, smart_adapter, adapter_base, etc.) into a single, generic adapter template that handles all use cases through template specialization and concepts.

Background (Why)

  • Multiple adapter classes exist with overlapping functionality
  • Users must choose between similar adapters with subtle differences
  • Each adapter has similar boilerplate code
  • Kent Beck's "Fewest Elements" rule: minimize API surface
  • Adding new adapter features requires changes to multiple classes

Current adapter proliferation:

// Current: Multiple similar adapters
typed_adapter<T>      // Type-safe adapter
smart_adapter<T>      // Smart pointer aware
adapter_base          // Base class for custom adapters
callback_adapter<T>   // For callback-based APIs

Scope (What)

Current State (Multiple Adapters)

include/kcenon/common/patterns/adapters/
├── adapter_base.h
├── typed_adapter.h
├── smart_adapter.h
├── callback_adapter.h
└── adapter_traits.h

Proposed State (Single Generic Adapter)

// patterns/adapters/adapter.h

// Adapter configuration via traits
template<typename T>
struct adapter_traits {
    using value_type = T;
    using pointer_type = T*;
    using reference_type = T&;
    static constexpr bool is_smart_pointer = false;
    static constexpr bool supports_weak = false;
};

// Specialization for shared_ptr
template<typename T>
struct adapter_traits<std::shared_ptr<T>> {
    using value_type = T;
    using pointer_type = std::shared_ptr<T>;
    using reference_type = T&;
    static constexpr bool is_smart_pointer = true;
    static constexpr bool supports_weak = true;
    using weak_type = std::weak_ptr<T>;
};

// Specialization for unique_ptr
template<typename T>
struct adapter_traits<std::unique_ptr<T>> {
    using value_type = T;
    using pointer_type = std::unique_ptr<T>;
    using reference_type = T&;
    static constexpr bool is_smart_pointer = true;
    static constexpr bool supports_weak = false;
};

// Single unified adapter
template<typename T, typename Traits = adapter_traits<T>>
class adapter {
public:
    using value_type = typename Traits::value_type;
    using pointer_type = typename Traits::pointer_type;
    
    // Construct from value
    explicit adapter(T value) : value_(std::move(value)) {}
    
    // Access
    auto get() const noexcept -> decltype(auto) {
        if constexpr (Traits::is_smart_pointer) {
            return value_.get();
        } else {
            return &value_;
        }
    }
    
    auto operator*() const -> typename Traits::reference_type {
        if constexpr (Traits::is_smart_pointer) {
            return *value_;
        } else {
            return value_;
        }
    }
    
    auto operator->() const noexcept {
        return get();
    }
    
    // Weak reference support (only for shared_ptr)
    auto weak() const requires (Traits::supports_weak) {
        return typename Traits::weak_type(value_);
    }
    
    // Check validity
    explicit operator bool() const noexcept {
        if constexpr (Traits::is_smart_pointer) {
            return static_cast<bool>(value_);
        } else {
            return true;  // Value types are always valid
        }
    }
    
    // Type-safe casting
    template<typename U>
    auto as() const -> std::optional<adapter<U>> {
        if constexpr (std::is_base_of_v<U, value_type>) {
            return adapter<U>(static_cast<U>(*get()));
        } else if constexpr (Traits::is_smart_pointer) {
            if (auto casted = std::dynamic_pointer_cast<U>(value_)) {
                return adapter<std::shared_ptr<U>>(std::move(casted));
            }
        }
        return std::nullopt;
    }
    
private:
    T value_;
};

// Deduction guides
template<typename T>
adapter(T) -> adapter<T>;

template<typename T>
adapter(std::shared_ptr<T>) -> adapter<std::shared_ptr<T>>;

template<typename T>
adapter(std::unique_ptr<T>) -> adapter<std::unique_ptr<T>>;

// Factory functions for convenience
template<typename T, typename... Args>
auto make_adapter(Args&&... args) {
    return adapter<T>(T(std::forward<Args>(args)...));
}

template<typename T, typename... Args>
auto make_shared_adapter(Args&&... args) {
    return adapter(std::make_shared<T>(std::forward<Args>(args)...));
}

Backward Compatibility

// Deprecated aliases
template<typename T>
using typed_adapter [[deprecated("Use adapter<T> instead")]] = adapter<T>;

template<typename T>
using smart_adapter [[deprecated("Use adapter<std::shared_ptr<T>> instead")]] = 
    adapter<std::shared_ptr<T>>;

Impact Analysis (Where)

Current File Lines Action
adapters/adapter_base.h ~80 Integrate into adapter<T>
adapters/typed_adapter.h ~120 Replace with adapter<T>
adapters/smart_adapter.h ~150 Replace with adapter<shared_ptr<T>>
adapters/callback_adapter.h ~100 Evaluate if still needed
New: adapters/adapter.h ~200 Unified implementation
Total ~450 ~200 (-56%)

Implementation Plan (How)

Phase 1: Core Adapter

  1. Create patterns/adapters/adapter.h
  2. Implement adapter_traits primary template
  3. Add shared_ptr specialization
  4. Add unique_ptr specialization
  5. Implement unified adapter<T> template

Phase 2: Features

  1. Add type-safe casting via as<U>()
  2. Add weak reference support (requires constraint)
  3. Add deduction guides
  4. Add factory functions

Phase 3: Migration

  1. Add deprecated aliases for old adapter types
  2. Update all internal usages to new adapter
  3. Update dependent systems (if any)

Phase 4: Cleanup

  1. Mark old adapter files as deprecated
  2. Update documentation
  3. Add migration guide

Acceptance Criteria

  • Single adapter<T> template handles all use cases
  • Smart pointer support via template specialization
  • No functionality loss compared to current adapters
  • Deprecated aliases provide backward compatibility
  • All existing tests passing
  • Documentation updated with new patterns

Labels

  • refactor
  • code-quality
  • api-design

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions