2017-03-29 10 views
1

이것은 깨지기 어려운 비트입니다. 나는C++에서 각 연산자가 과부하 캐스케이드 후에 개행 문자를 쓰려고 할 때

그래서 기본적 목표는

log << "text"; // This would output "text\n"; 
    log << "some " << "text"; //This would output "some text\n" 
    log << "some number "<< 5; //This would output "some number 5\n" 

을 주석으로 내가 사용 해봤 않는 다음과 같은 코드를 가지고있다 :: cout을 성병과 비슷한 모양을 가지고 노력하고 로깅 기능을 쓰고 있어요 variadic 템플릿,하지만 멀리 가지 않았다.

#include <iostream> 

// stream_newliner works by just taking the address of an ostream you 
// give it, and forwarding all writes (via operator<<) to that ostream. 
// Then, when it finally destructs, it writes the newline. 
template <typename CharT, typename Traits> 
class stream_newliner 
{ 
public: 
// The constructor just takes the address of the stream you give it. 
explicit stream_newliner(std::basic_ostream<CharT, Traits>& s) : 
    p_s_{&s} 
{} 

// Boilerplate move construction. Nothing special here. 
stream_newliner(stream_newliner&& s) : p_s_{nullptr} 
{ 
    *this = std::move(s); 
} 

// On destruction, write the newline. (Note we check the pointer for 
// null for technical reasons.) 
~stream_newliner() 
{ 
    if (p_s_) 
    { 
     // If you have std::unhandled_exceptions(), you can check to 
     // see if a new exception is in flight, and only do the 
     // newline if there's not. 

     // Note that I'm writing an X and not a newline so you can 
     // see it easier. 
     (*p_s_) << 'X'; 
    } 
} 

// This is where the magic happens. Any time you do "x << y" where 
// x is this type, it just passes it on to the underlying stream. 
// When you do "x << y1 << y2", it all works magically because it 
// translates to "operator<<(operator<<(x, y1), y2)"... and this 
// function just passes each call to the underlying stream. 
template <typename T> 
auto operator<<(T&& t) -> stream_newliner<CharT, Traits>& 
{ 
    (*p_s_) << std::forward<T>(t); 
    return *this; 
} 

// Boilerplate move assignment. Nothing special here. 
auto operator=(stream_newliner&& s) -> stream_newliner& 
{ 
    std::swap(p_s_, s.p_s_); 
    return *this; 
} 

// Technically don't need these, but it doesn't hurt to be explicit. 
stream_newliner(stream_newliner const&) = delete; 
auto operator=(stream_newliner const&) = delete; 

    private: 
// You could use a reference to the stream rather than a pointer, 
// but references are harder to work with as class members. 
std::basic_ostream<CharT, Traits>* p_s_ = nullptr; 
    }; 

    // Helper function to make it easier to create. 
    template <typename CharT, typename Traits> 
    auto line(std::basic_ostream<CharT, Traits>& s) 
    { 
return stream_newliner<CharT, Traits>{s}; 
    } 

    auto main() -> int 
    { 
line(std::cout) << "foo"; 
std::cout << '\n'; 

line(std::cout) << 1 << ' ' << 2 << ' ' << 3; 
std::cout << '\n'; 
    } 
+0

당신은 할 수 없습니다. 각각의'<< "는 연산자에 대한 별도의 호출이므로 체인의 마지막 시점을 알 수있는 방법이 없습니다. – Barmar

답변

2

나는이 너무 까다로운 아니라고 생각 : 여기에 지금까지 가장 성공적인 시도입니다. 보자 :

struct eol_log 
{ 
    eol_log(std::ostream& os) : os_(os) {} 
    ~eol_log() { os_ << '\n'; } 

    template <typename T> 
    std::ostream& operator<<(const T& t) && { return os_ << t; } 

    std::ostream& os_; 
}; 

사용법 : "전체 표현 가드"의 일종으로

eol_log(std::cout) << "Foo" << "Bar" << 10; 
eol_log(std::cout) << 10; 

임시 객체의 역할을합니다.

오히려 로그 싱크 역할을하는 고정 된 물체를 원하는 경우, 클래스 위를 포장 할 수 있지만, 지금은 체인을 따라 마지막 요소가되는 상태를 이동해야합니다

class EolLogger 
{ 
    class RvalLog 
    { 
     friend class EolLogger; 

     RvalLog(std::ostream* os) : os_(os) {} 
     RvalLog(RvalLog&& rhs) : os_(rhs.os_) { rhs.os_ = nullptr; } 
     std::ostream* os_; 

    public: 
     template <typename T> 
     RvalLog operator<<(const T& t) && { *os_ << t; return std::move(*this); } 

     ~RvalLog() { if (os_) *os_ << '\n'; } 
    }; 

    std::ostream& os_; 

public: 
    EolLogger(std::ostream& os) : os_(os) {} 

    template <typename T> 
    RvalLog operator<<(const T& t) { return RvalLog(&os_) << t; } 
}; 

사용법 :

EolLogger elog(std::cout); 

elog << "Foo"; 
elog << 1 << 2 << 3; 
+0

[데모] (https://wandbox.org/permlink/kttlK6zHUn8Q9UxH) –

+0

아마도 'with_eol' 또는 그와 같은 이름으로 불렀을 것입니다. –

+0

'eol_log'는 Elf Tolkien이 빠진 것처럼 들립니다. – user4581301

1

사람이 게시하고, 그렇다고 작업을 수행합니다.

#include <iostream> 
using namespace std; 

struct Liner { 
    bool Owned = true; 
    Liner() = default; 
    Liner(Liner &&O) { O.Owned = false; } 
    ~Liner() { if (Owned) std::cout << '\n'; } 
}; 

template <typename T> 
const Liner &operator<<(const Liner &L, T &&E) { 
    std::cout << std::forward<T>(E); 
    return L; 
} 

struct SubLiner { 
    operator Liner() const { return {}; } 
}; 

const static SubLiner line; 

int main() { 
    line << "hello " << "world!"; 
    line << 1 << 2; 
    line << 3; 
    return 0; 
}