2017-10-16 148 views
7

我有这样的函数:如何通过前N个参数传递给C++函数

void loadData(std::function<void (std::string, std::string, std::string)> callback) 
{ 
    // data loading stuff 
    callback(body, subject, header); 
} 

的问题是,我并不一定需要在我的回调函数使用subjectheader。现在我处理这样说:

loadData([](std::string body, std::string, std::string){ 
    std::cout << body; 
}) 

我想

loadData([](std::string body){ 
    std::cout << body; 
}) 

来取代它,并自动传递给回调函数能够接受尽可能多的参数,因为它。 我不想为所有3个可能的参数计数手动重载loadData函数。我也不想在调用网站上使用任何更复杂的lambda语法,因为我的库应该清楚供其他人使用。 这可能使用C++ STL和Boost?

+1

为什么不超载的?或者只是忽略其他两个参数 – Mgetz

+1

@Mgetz,因为我相信我可以再次面对这个问题,当我有3个以上的参数,并且我在这里有更多的重载,更糟糕的是我的代码变得越来越不可读。 –

+0

我认为有一些原因,你不想把参数放在'struct'中传递? –

回答

1

我被其他的答案,其中提出要作出这样的传递参数的函子的正确数量的包装的一个启发。我觉得这个解决方案非常好,并且认为我会尝试制作一个通用的模板包装器,其中参数的数量不是硬编码的。这是我想出的:

#include <string> 
#include <functional> 
#include <iostream> 

struct WrapperHelp 
{ 
    template 
     < typename L 
     , typename Tuple 
     , std::size_t... Is 
     , typename... Ts 
     > 
    static auto apply(L&& l, Tuple t, std::index_sequence<Is...>, Ts&&... ts) 
     -> decltype(l(std::get<Is>(t)...)) 
    { 
     return l(std::get<Is>(t)...); 
    } 

    template 
     < typename L 
     , typename Tuple 
     , std::size_t... Is 
     , typename T1 
     , typename... Ts 
     > 
    static auto apply(L&& l, Tuple t, std::index_sequence<Is...>, T1&& t1, Ts&&... ts) 
     -> decltype(WrapperHelp::apply(std::forward<L>(l), std::forward_as_tuple(std::get<Is>(t)..., t1), std::make_index_sequence<sizeof...(Is) +1 >(), ts...)) 
    { 
     return WrapperHelp::apply(std::forward<L>(l), std::forward_as_tuple(std::get<Is>(t)..., t1), std::make_index_sequence<sizeof...(Is) + 1>(), ts...); 
    } 
}; 

template<typename L> 
struct OptionalWrapper { 
    public: 
     OptionalWrapper(L l) : lambda{std::move(l)} {} 

     template<typename... Ts> 
     void operator()(Ts&&... ts) const 
     { 
     WrapperHelp::apply(lambda, std::tuple<>(), std::index_sequence<>(), std::forward<Ts>(ts)...); 
     } 

    private: 
     L lambda; 
}; 

template<typename L> 
auto makeOptionalWrapper(L l) { return OptionalWrapper<L>{std::move(l)}; } 

template<class F> 
void loadData(OptionalWrapper<F>&& callback) 
{ 
    std::string body = "body"; 
    std::string subject = "subject"; 
    std::string header = "header"; 
    double lol = 2.0; 
    callback(body, subject, header, lol); 
} 

template<typename L> 
void loadData(L callback) 
{ 
    loadData(makeOptionalWrapper(std::move(callback))); 
} 

int main() { 
    //apply(std::tuple<double>(2), std::tuple<double>(2)); 
    loadData([](auto&& body) { 
     std::cout << body << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject) { 
     std::cout << body << " " << subject << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject, auto&& header) { 
     std::cout << body << " " << subject << " " << header << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject, auto&& header, auto&& lol) { 
     std::cout << body << " " << subject << " " << header << " " << lol << std::endl; 
    }); 
    return 0; 
} 

这应该适用于任何功能,任何数量的“可选”参数,以及任何类型的参数。这是不是最漂亮的代码,但我希望这个想法是清楚的,可以是一些使用:)

Live example

+0

是的,这正是我所寻找的。非常感谢。 –

+1

@DenisSheremet没问题,很高兴能够协助:) – Banan

6

那么使用...忽略以下参数呢?

loadData([](std::string body, ...){ 
    std::cout << body; 
}) 


正如指出的说书人(感谢!)使用省略号可以是不支持的不平凡的类型(见 [expr.call]p9有详细介绍)。要避免此问题,如果您可以使用C++ 14,则可以使用 auto ...(更好的 auto && ...以避免不必要的副本;谢谢雅克)。

loadData([](std::string body, auto && ...){ 
    std::cout << body; 
}) 
+3

传递任何东西,但最平凡的类型只是有条件地支持。 – StoryTeller

+0

@StoryTeller - 不确定要理解;你的意思是''...保证只支持微不足道的类型? – max66

+0

http://eel.is/c++draft/expr.call#9 – StoryTeller

1

你可以围绕lambda做一个包装。

template<typename L> 
struct OptionalWrapper { 
    OptionalWrapper(L l) : lambda{std::move(l)} {} 

    void operator()(std::string body, std::string subject, std::string header) const { 
     call(lambda, body, subject, header); 
    } 

private: 
    template<typename T> 
    auto call(T& l, std::string body, std::string subject, std::string header) const 
     -> decltype(l(body, subject, header)) 
    { 
     return l(body, subject, header); 
    } 

    template<typename T> 
    auto call(T& l, std::string body, std::string subject, std::string) const 
     -> decltype(l(body, subject)) 
    { 
     return l(body, subject); 
    } 

    template<typename T> 
    auto call(T& l, std::string body, std::string, std::string) const 
     -> decltype(l(body)) 
    { 
     return l(body); 
    } 

    L lambda; 
}; 

template<typename L> 
auto makeOptionalWrapper(L l) { return OptionalWrapper<L>{std::move(l)}; } 

然后,用你的包装那样:

void loadData(std::function<void (std::string, std::string, std::string)> callback) 
{ 
    callback(body, subject, header); 
} 

template<typename L> 
void loadData(L callback) 
{ 
    loadData({makeOptionalWrapper(std::move(callback))}); 
} 
相关问题