79483725

Date: 2025-03-04 12:43:11
Score: 1.5
Natty:
Report link

Thanks to everyone who provided an answer, and special thanks to @HolyBlackCat for pointing out the restriction on the standard library—I wasn't aware of it.

After some thought, I've come up with a solution that meets my needs and would like to share it with you. Please feel free to critique it if I’ve overlooked any potential issues or if it breaks any C++ standard restrictions.

The code below supports basic types like std::pair, std::tuple, and std::array, as well as my custom specializations, to the best of my understanding.

#include <iostream>
#include <algorithm>
#include <string>
#include <array>
#include <tuple>
#include <functional>

// Define a simple struct MyStruct with two different types
struct MyStruct { int i; double d; };

struct MyOtherStruct { int i; double d; std::string s; };

// Specialize std::tuple_size for MyStruct to enable tuple-like behavior
namespace std {
    // Standard allows specialization of std::tuple_size for user-defined types
    template <> struct tuple_size<MyStruct> : std::integral_constant<std::size_t, 2> { };  // MyStruct has 2 members

    template <> struct tuple_size<MyOtherStruct> : std::integral_constant<std::size_t, 3> { };  // MyOtherStruct has 3 members
}

namespace My {
    // Support for all std::tuple-like types using std::apply
    template <std::size_t N, typename StdStruct>  
    constexpr decltype(auto) Get(const StdStruct& a) { 
        return std::get<N>(a); 
    }
    template <std::size_t N, typename StdStruct>  
    constexpr decltype(auto) Get(StdStruct& a) { 
        return std::get<N>(a); 
    }
    template <std::size_t N, typename StdStruct>  
    constexpr decltype(auto) Get(StdStruct&& a) { 
        return std::get<N>(a); 
    }

    // Specialization of Get for MyStruct to access its members
    template <std::size_t N>  
    constexpr decltype(auto) Get(const MyStruct& a) { 
        if constexpr (N == 0) 
            return (a.i); 
        else if constexpr (N == 1) 
            return (a.d); 
    }

    // Specialization of Get for MyOtherStruct to access its members
    template <std::size_t N>  
    constexpr decltype(auto) Get(const MyOtherStruct& a) { 
        if constexpr (N == 0) 
            return (a.i); 
        else if constexpr (N == 1) 
            return (a.d); 
        else if constexpr (N == 2) 
            return (a.s); 
    }

    // Convert a struct to a tuple using index sequence as someone else suggested
    template <typename Tuple, std::size_t... I> 
    constexpr auto ToTupleImpl(Tuple&& t, std::index_sequence<I...>) { 
        return std::make_tuple(Get<I>(t)...); 
    }

    // Public interface to convert a struct to a tuple
    template <typename Tuple> 
    constexpr auto ToTuple(const Tuple& s) { 
        return ToTupleImpl(s, std::make_index_sequence<std::tuple_size<Tuple>::value>()); 
    }

    // Implementation of Apply to invoke a callable with tuple elements
    template <class Callable, class Struct, size_t... Indices>
    constexpr decltype(auto) Apply_impl(Callable&& Obj, Struct&& Strct, std::index_sequence<Indices...>) noexcept(
        noexcept(std::invoke(std::forward<Callable>(Obj), Get<Indices>(std::forward<Struct>(Strct))...))) {
        return std::invoke(std::forward<Callable>(Obj), Get<Indices>(std::forward<Struct>(Strct))...);
    }

    // Public interface to apply a callable to a tuple-like structure
    template <class Callable, class Struct>
    constexpr decltype(auto) Apply(Callable&& Obj, Struct&& Strct) noexcept(
        noexcept(Apply_impl(std::forward<Callable>(Obj), std::forward<Struct>(Strct), std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Struct>>>{}))) {
        return Apply_impl(std::forward<Callable>(Obj), std::forward<Struct>(Strct),
            std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Struct>>>{});
    }
}

int main() {
    // Example usage of MyStruct
    constexpr MyStruct ms{42, 3.14};
    const MyOtherStruct mos {42, 3.14, "My other struct"};

    // Apply a lambda to MyStruct converted to a tuple
    My::Apply([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, My::ToTuple(ms));
    
    // Apply a lambda to a std::pair
    My::Apply([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, std::pair {2, 3});
    
    // Apply a lambda to a std::array
    My::Apply([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, std::array {4, 5});
    
    // Apply a lambda directly to MyStruct
    My::Apply([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, ms);
    
    // Apply a lambda directly to MyOtherStruct
    My::Apply([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, mos);

    return 0;
}
Reasons:
  • Blacklisted phrase (0.5): Thanks
  • Blacklisted phrase (0.5): thanks
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @HolyBlackCat
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: Catriel