vendredi 22 mai 2015

Default parameters for polymorphic lambda

I'm trying to write a macro that would shorten lambda expression syntax when single return statement is all it's needed. My first attempt looked like this:

struct void_t{};                                                                                                                                     

#define LR(EXPR) (auto&& p_1 = void_t{}, auto&& p_2 = void_t{}, auto&& p_3 = void_t{}){ return EXPR; }

Unfortunately it does not work as I would expect. Default arguments are basically ignored and I cannot call this lambda with less than three params:

std::cout << []LR(p_1)("test"); // compile error                                                                                                     
std::cout << []LR(p_1 + p_2)(2, 3); // compile error                                                                                                 
std::cout << []LR(p_1 + p_2)(std::string("hello "), std::string(" world!")); // compile error                                                        
std::cout << []LR(p_1 + p_2)(2, 3, 0); // OK                                                                          

I managed to devise some convoluted workaround that seemed to work but it has issues:

template<class LT> struct lambda_wrapper                                        
{                                                                               
    lambda_wrapper(LT p_lambda): m_lambda(p_lambda){}                           

    template<class T1, class T2, class T3>                                      
    auto operator()(T1&& p_1, T2&& p_2, T3&& p_3) const                         
    {                                                                           
        return m_lambda(std::forward<T1>(p_1),                                  
                        std::forward<T2>(p_2),                                  
                        std::forward<T3>(p_3));                                 
    }                                                                           

    template<class T1, class T2> auto operator()(T1&& p_1, T2&& p_2) const      
    {                                                                           
        return m_lambda(std::forward<T1>(p_1), std::forward<T2>(p_2), void_t{});
    }                                                                           

    template<class T1> auto operator()(T1&& p_1) const                          
    {                                                                           
        return m_lambda(std::forward<T1>(p_1), void_t{}, void_t{});             
    }                                                                           

    auto operator()() const                                                     
    {                                                                           
        return m_lambda(void_t{}, void_t{}, void_t{});                          
    }                                                                           
private:                                                                        
    LT m_lambda;                                                                
};                                                                              

template <class LT> lambda_wrapper<LT> operator++(LT&& p_lambda, int)           
{                                                                               
    return {std::forward<LT>(p_lambda)};                                        
}                                                                               

#define LR(EXPR) (auto&& p_1, auto&& p_2, auto&& p_3){ return EXPR; }++         

Anybody has a better idea?

UPDATE:

Expanding upon your contributions I came up with following implementation:

struct void_t{};

template<class TT> constexpr void_t& get_param(void_t, const TT&, void_t& p_void)
{
    return p_void;
}

template<size_t I, class TT>
constexpr auto get_param(std::integral_constant<size_t, I>, const TT& p_tuple, void_t& p_void)
-> std::tuple_element_t<I, TT>&
{
    return std::get<I>(p_tuple);
}

#define LR(EXPR) (auto&&... ps) \
{\
    auto l_tuple_sflj123 = std::forward_as_tuple(ps...);\
    typedef typename std::conditional<sizeof...(ps) >= 1, std::integral_constant<size_t, 0>, void_t>::type type_1;\
    typedef typename std::conditional<sizeof...(ps) >= 2, std::integral_constant<size_t, 1>, void_t>::type type_2;\
    typedef typename std::conditional<sizeof...(ps) >= 3, std::integral_constant<size_t, 2>, void_t>::type type_3;\
    void_t l_void_pfjaos43{};\
    auto&& p_1 = get_param(type_1{}, l_tuple_sflj123, l_void_pfjaos43);\
    auto&& p_2 = get_param(type_2{}, l_tuple_sflj123, l_void_pfjaos43);\
    auto&& p_3 = get_param(type_3{}, l_tuple_sflj123, l_void_pfjaos43);\
    return EXPR;\
}

int main() {
    std::cout << []LR(p_1)("test ", "test2");
    std::cout << []LR(p_1 + p_2)(std::string("Hallo "), std::string("World! "));
    int l_1 = 4;
    std::cout << [=]LR(p_1 + p_2 + p_3 + l_1)(1, 2, 3);
}

Can you see any problems with this?

Aucun commentaire:

Enregistrer un commentaire