2017-10-16 24 views
7

funkcją Mam funkcję tak:Jak przekazać pierwsze args N do C++

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

Problemem jest to, nie jestem koniecznie trzeba używać subject i header w mojej funkcji wywołania zwrotnego. Teraz jestem posługiwaniu się nim w ten sposób:

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

chcę go zastąpić

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

i automatycznie przejść do funkcji callback jak wiele argumentów, jak to w stanie zaakceptować. Nie chcę ręcznie przeciążać funkcji loadData dla wszystkich 3 możliwych argumentów. Nie chcę również używać bardziej skomplikowanej składni lambda w witrynie wywołującej, ponieważ moja biblioteka powinna być czytelna dla innych użytkowników. Czy jest to możliwe przy użyciu C++ STL i Boost?

+1

Dlaczego nie po prostu przeciążenie? lub po prostu zignoruj ​​dwa pozostałe parametry: – Mgetz

+1

@Mgetz, ponieważ uważam, że mogę ponownie stawić czoła temu problemowi, gdy będę miał więcej niż 3 argumenty, a im więcej przeciążenia mam tutaj, tym brzydszy i mniej czytelny będzie mój kod. –

+0

Zakładam, że jest jakiś powód, dla którego nie chcesz umieszczać argumentów w 'struct' do przekazania? –

Odpowiedz

1

mam inspirowane przez jednego z pozostałych odpowiedzi, który zamierza zrobić owijkę, która przechodzi prawidłową liczbę parametrów do funktora. Uważam, że to rozwiązanie jest bardzo przyjemne i pomyślałem, że spróbuję stworzyć szablonową wrapper, w którym liczba argumentów nie jest sztywno zakodowana. Oto co wymyśliłem:

#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; 
} 

to powinno działać dla każdej funkcji, z dowolną liczbą parametrów „opcjonalne”, a także z wszelkich typów parametrów. To nie jest najładniejszy kod, ale mam nadzieję, że idea jest jasna i może się przydać :)

Live example

+0

Tak, dokładnie tego szukałem. Wielkie dzięki. –

+1

@DenisSheremet Bez problemu, cieszę się z pomocy :) – Banan

6

Co z ignorowaniem poniższych argumentów przy użyciu ...?

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


Jak wskazano przez Narratora (dzięki!) Stosowanie elipsy może być nieobsługiwane dla niezarejestrowanych typów trywialne (patrz [expr.call]p9 więcej szczegółów).

Aby uniknąć tego problemu, jeśli możesz używać C++ 14, możesz użyć auto ... (lepiej , aby uniknąć niepotrzebnych kopii, dzięki Yakk).

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

Podawanie niczego poza najbardziej trywialnymi typami jest tylko warunkowo obsługiwane. – StoryTeller

+0

@StoryTeller - nie rozumiem; masz na myśli, że '...' gwarantuje, że obsługuje tylko trywialne typy? – max66

+0

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

1

Można zrobić opakowanie wokół 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)}; } 

Następnie użyć otoki tak:

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))}); 
} 
Powiązane problemy