2014-07-24 27 views
5

Próbuję napisać operator strumienia <<, który może wysyłać do std::tuple strumieni zamiast jednego strumienia. Więc w zasadzie, próbuję napisać tee polecenie Uniksa w C++, a nie:Jak wyprowadzać wartości do krotki strumieni w C++ 11

std::tie(std::cout,std::clog) << 1; 

Próbowałem napisać operatora strumień rekurencyjnie za pomocą zmiennej liczbie argumentów szablonu programowania w C++ 11. To, co do tej pory miałem, jest takie, jak w poniższym kodzie. Ale kod nie kompiluje się, a komunikat o błędzie jest dość długi.

Moje pytanie brzmi: jak naprawić kod, aby działał?

Pierwszy komunikat o błędzie z kompilacji z g++ -std=c++11 (gcc-4.8.1) była następująca:

test.cpp:24:33: error: no match for 'operator<<' (operand types are 'std::tuple<std::basic_ostream<char, std::char_traits<char> >&, std::basic_ostream<char, std::char_traits<char> >&>' and 'int') 
    std::tie(std::cout,std::cout) << 1; 

PS: Szukałem SO, a jest kilka postów dotyczących pisania obiektu złożonego strumienia. Kod obejmuje wiele wewnętrznych elementów stream i streambuf. To, czego tu szukam, to proste/naiwne rozwiązanie w kilkunastu liniach do wykonania podobnego zadania.

Dzięki


Mój kod:

#include <iostream> 
#include <tuple> 

template<typename _Value, typename Head, typename ... Tail> 
struct _tee_stream { 
    static std::tuple<Head,Tail...>& _print(std::tuple<Head,Tail...>& _s, const _Value& _t) { 
    return std::make_tuple(std::get<0>(_s) << _t ,_tee_stream<Tail...,_Value>::_print(_s,_t)); 
    } 
}; 

template<typename _Value> 
struct _tee_stream<_Value, std::tuple<>> { 
    static std::tuple<>& _print(std::tuple<>& _s, const _Value& _t) { 
    return _s; 
    } 
}; 

template< typename _Value, typename... _TElements> 
std::tuple<_TElements...>& operator<<(std::tuple<_TElements...>& _s, const _Value& _t) { 
    return _tee_stream<_Value, std::tuple<_TElements...>>::_print(_s, _t); 
}; 

int main() { 
    std::tie(std::cout,std::cout) << 1; //no compile 
    std::make_tuple(std::cout,std::cout) << 1; //no compile either 
} 

UPDATE: poniżej jest co pracował dla mnie na podstawie użytkownika @ KerrekSB przykład kodu:

#include <iostream> 
#include <tuple> 
#include <utility> 
#include <fstream> 


template <unsigned int N> 
struct tee_stream 
{ 
    template <typename ...Args, typename T> 
    static std::tuple<Args...> & print(std::tuple<Args...> & t, T && x) 
    { 
    std::get<sizeof...(Args) - N>(t) << x; 
    tee_stream<N - 1>::print(t, std::forward<T>(x)); 
    return t; 
    } 
}; 

template <> 
struct tee_stream<0> 
{ 
    template <typename ...Args, typename T> 
    static std::tuple<Args...> & print(std::tuple<Args...> &, T &&) {} 
}; 

template <typename ...Args, typename T> 
std::tuple<Args...> & operator<<(std::tuple<Args...> & t, T && x) 
{ 
    return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x)); 
} 

template <typename ...Args, typename T> 
std::tuple<Args...> & operator<<(std::tuple<Args...> && t, T && x) 
{ 
    return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x)); 
} 

int main() 
{ 
    std::ofstream os("a.txt"); 
    auto t = std::tie(std::cout, os); 
    t << "Foo" << "Bar\n"; 
    std::tie(std::cout, os) << "Foo" << "Bar\n"; 
} 

http://ideone.com/fBXuvP


@ Jarod42,

dzięki. Twój kod wykonuje rekurencję/pętlę znacznie bardziej zwinnie niż mój prototyp i naprawia użycie typów referencyjnych w moim kodzie. Oryginalne przypadki testowe działają tak, jak pokazało się Twoje demo. Jednak nadal nie mogę skompilować Twojej wersji, jeśli użyję w strumieniu std::endl (zamiast "Foo") lub std::ofstream (zamiast std::cout), jak pokazano poniżej w wierszach "nie OK". Każdy pomysł, jak je naprawić?

#include <iostream> 
#include <fstream> 
#include <tuple> 

#if 1 // Not in C++11 // make_index_sequence 
#include <cstdint> 

template <std::size_t...> struct index_sequence {}; 

template <std::size_t N, std::size_t... Is> 
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {}; 

#endif // make_index_sequence 

namespace detail 
{ 
    template <typename Tuple, typename T, std::size_t...Is> 
    Tuple output(const Tuple& t, const T& x, index_sequence<Is...>) { 
    return Tuple{(std::get<Is>(t) << x)...}; 
    } 
} 

template <typename ...Args, typename T> 
std::tuple<Args&...> operator<<(const std::tuple<Args&...>& t, const T& x) { 
    return detail::output(t, x, make_index_sequence<sizeof...(Args)>()); 
} 

int main() { 
    std::ofstream os("aa.txt"); 
    os << "Hi" << std::endl; 

    std::tie(std::cout, std::cout) << "Foo" << "Bar"; //OK 
    std::tie(std::cout, std::cout) << "Foo" << "Bar" << std::endl; //not OK on endl 
    std::tie(std::cout, os) << 1 << "Foo" << "Bar"; //not OK on ofstream 

    return 0; 
} 
+0

Używasz [zastrzeżone identyfikatory] (http://stackoverflow.com/questions/228783/what-are-the-rules- o użyciu-podkreślenia-w-ac-identyfikatorze) i nie powinny przeciążać operatorów dla standardowych typów. – chris

+0

Ponieważ 'tie' zwraca tymczasowy, prawdopodobnie chcesz, aby twoje przeciążenie wzięło referencję rvalue. –

+3

Oto [niezbyt skomplikowane rozwiązanie] (http://ideone.com/RYPOGw). –

Odpowiedz

1

Możesz użyć następujących:

#if 1 // Not in C++11 // make_index_sequence 
#include <cstdint> 

template <std::size_t...> struct index_sequence {}; 

template <std::size_t N, std::size_t... Is> 
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {}; 

#endif // make_index_sequence 

// Helper. C++11 cannot use: auto f() { return foo(); } 
// Usage: auto f() -> Return(foo()) 
#define Return(ret) decltype(ret) { return ret; } 

namespace detail 
{ 
    template <typename Tuple, typename T, std::size_t...Is> 
    auto output(const Tuple& t, const T& x, index_sequence<Is...>) -> 
    Return (std::tie(std::get<Is>(t) << x...)) 
} 

template <typename ...Args, typename T> 
auto operator<<(const std::tuple<Args&...>& t, const T& x) -> 
Return(detail::output(t, x, make_index_sequence<sizeof...(Args)>())) 

live example

+0

Wycofałem się z Twojej decyzji, ponieważ nie wyjaśniłeś, na czym polegał problem z kodem OP lub w jaki sposób twój kod go naprawia. – Puppy

+0

@ Jarod42, +1: Wielkie dzięki. Twój kod rozwiązuje pierwotny problem i poprawia błędy w moim prototypowym przykładzie. Ale nie działa na 'std :: endl' lub' ofstream'. Zobacz moją aktualizację. – tinlyx

+0

@ TingL: dla 'std :: endl', jest to normalne, jest to funkcja szablonu i nie wymaga krotki jako argumentu (patrz http://en.cppreference.com/w/cpp/io/ manip/endl). – Jarod42

0

Jednym z problemów jest argument: std::tuple<_TElements...>& _s.
Od czasu próby użycia wartości operator << z wartościami tymczasowymi zwróconymi przez std::tie i std::make_tuple, pojawia się błąd kompilatora, ponieważ nie można powiązać tymczasowego z referencją.

Mimo to zadziałałaby wartość odniesienia (std::tuple<_TElements...> const& _s).
(Można również użyć wartości rvalue)

Nie używaj nazw zaczynających się od podkreślenia (_); są one zarezerwowane dla wdrożenia.

+1

Reguły zarezerwowanego identyfikatora są bardziej skomplikowane. Na przykład "_s" nie jest używane niewłaściwie. – chris

+0

To prawda, i ta złożoność powinna być dobrym powodem, aby po prostu unikać podkreśleń na początku identyfikatorów zupełnie imo. Chodzi mi o to, że tak naprawdę nic ci nie dają = \ –

+0

To prawda, myślę, że jest może jedno konkretne miejsce, w którym mógłbym go użyć. – chris