Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.144.187.103] |
|
Страницы: (4) [1] 2 3 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Всем привет.
В общем есть лог файл, хочу выводить в него инфу как в потоки. Все нормально и отлично, но из за специфики дизайна, что то встал на написании манипулятора. Я умом понимаю что он хочет, и что нужно сделать, но не пойму как. По задумке я хочу писать вот так: Logger log; log << "param1" << 1 << "ololo" << io::endl; И оно все пишет и работает, но не вызывается манипулятор io::endl; Он нужен для того, чтобы сбросить содержимое внутреннего потока в файл. Если например переписать вот так: Logger log; log << "param1" << 1 << "ololo" << L'\n'; log.flush(); То все работает просто великолепно, как и задумывалось. Но это же нужно писать вырвиглазный L'\n' + log.flush делать. Конкретно эти две строчки я хотел запихнуть в io::endl манипулятор, и мне это почти удалось. Но есть одна загвоздка, не могу понять как перегрузить << для WrappedStream, чтоб он вызвал нужную мне версию. У меня при любом раскладе вызывается в logger << вот это -> из класса Logger template<class T> WrappedStream<std::wostringstream> operator<<(const T& arg) { return WrappedStream<std::wostringstream>(m_stream, m_streammtx) << arg; } А при последующих вызовах, вызывается << у WrappedStream template<typename T> WrappedStream& operator<<(const T& arg) { if(m_gstream != nullptr) m_gstream->write(arg); return *this; } Надо ему как то сказать что хорошо было бы вызвать friend WrappedStream<T> operator<< (Logger& stream, const std::function<WrappedStream<T>(Logger&)>& pFn); Когда в него передают io::endl Но я не пойму как, я уже все перепробовал. В итоге в лучше случае выводится адресс функции io::endl, вместо того, чтобы она выполнялась. Где я ступил? Спасибо, код прилагаю template<typename TStream> class GuardStream { public: GuardStream(const GuardStream&) = delete; GuardStream(GuardStream&&) = delete; GuardStream& operator=(const GuardStream&) = delete; GuardStream& operator=(GuardStream&&) = delete; GuardStream(TStream& stream, std::mutex& mtx) : m_stream(stream), m_mutex(mtx) { } ~GuardStream() { m_stream.flush(); } template<typename Arg> void write(const Arg& arg) { m_stream << arg; } private: std::lock_guard<std::mutex> m_mutex; TStream& m_stream; }; template<typename TStream> class WrappedStream { public: WrappedStream(WrappedStream&& rhs) { if (rhs.m_gstream != nullptr) m_gstream.swap(rhs.m_gstream); } WrappedStream(const WrappedStream& rhs) { if (rhs.m_gstream != nullptr) m_gstream.swap(rhs.m_gstream); } WrappedStream& operator=(WrappedStream&&) { if (this != &rhs && rhs.m_gstream != nullptr) m_gstream.swap(rhs.m_gstream); } WrappedStream& operator=(const WrappedStream&) { if (this != &rhs && rhs.m_gstream != nullptr) m_gstream.swap(rhs.m_gstream); } WrappedStream(TStream& stream, std::mutex& mutex) : m_gstream( std::make_unique<GuardStream<TStream>>(stream, mutex) ) { } template<typename T> WrappedStream& operator<<(const T& arg) { if(m_gstream != nullptr) m_gstream->write(arg); return *this; } template<typename T> friend WrappedStream<T> operator<< (Logger& stream, const std::function<WrappedStream<T>(Logger&)>& pFn); private: mutable std::unique_ptr<GuardStream<TStream>> m_gstream; }; template<typename T> WrappedStream<T> operator<< (Logger& stream, const std::function<WrappedStream<T>(Logger&)>& pFn) { return pFn(stream); } class Logger { public: Logger(const std::wstring& logname, unsigned int cachesize=100); void flush(); template<typename T> WrappedStream<std::wostringstream> operator<<(const std::function<WrappedStream<T>(Logger&)>& arg) { return WrappedStream<std::wostringstream>(m_stream, m_streammtx) << arg; } template<class T> WrappedStream<std::wostringstream> operator<<(const T& arg) { return WrappedStream<std::wostringstream>(m_stream, m_streammtx) << arg; } //template<> //WrappedStream<std::wostringstream> operator<<(const Logger& arg) //{ // return WrappedStream<std::wostringstream>(m_stream, m_streammtx) << arg; //} }; template<class T> inline auto operator<< (WrappedStream<std::wostringstream>& stream, const T& pFn) -> decltype(pFn(stream)) { return pFn(stream); } inline WrappedStream<std::wostringstream> operator<<(Logger& logger, std::function<WrappedStream<std::wostringstream>(Logger&)>& s) { return s(logger); } namespace io { inline WrappedStream<std::wostringstream> endl(Logger& rhs) { WrappedStream<std::wostringstream> stream(rhs.m_stream, rhs.m_streammtx); //! Так делать нельзя!!! нужно найти другой способ!!! stream << L'\n'; rhs.flush(); return stream; } |
Сообщ.
#2
,
|
|
|
Ты уверен, что код полный?
|
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Ты уверен, что код полный? Не, он обрезанный. Тут реализация всех функций/методов - которые участвуют в формировании лога. Щас попробую Сделать работоспособным этот кусок. Добавлено Ну вот допилил до тестового примера: namespace test { struct GuardStream { GuardStream() = delete; GuardStream(const GuardStream&) = delete; void operator=(const GuardStream&) = delete; GuardStream(std::ostream& ostream, std::mutex& mutex) : m_stream(ostream), m_gmtx(mutex) { } ~GuardStream() { m_stream.flush(); } template<typename T> void write(const T& x) { m_stream << x; } std::ostream& m_stream; std::lock_guard<std::mutex> m_gmtx; }; struct WrappedStream { WrappedStream() = delete; void operator=(const WrappedStream&) = delete; WrappedStream(std::ostream& ostream, std::mutex& mutex) : m_gstream( std::make_unique<GuardStream>(ostream, mutex)) { } WrappedStream(const WrappedStream& rhs) { m_gstream.swap(rhs.m_gstream); } template<typename T> WrappedStream& operator<<(const T& x) { m_gstream->write(x); return *this; } mutable std::unique_ptr<GuardStream> m_gstream; }; struct Logger { explicit Logger(std::ostream& ostream) : ostream_(ostream) { } WrappedStream flush() { return WrappedStream(ostream_, mutex_) << "flush"; } template<typename T> WrappedStream operator<<(const T& x) { return WrappedStream(ostream_, mutex_) << x; } std::ostream& ostream_; std::mutex mutex_; }; template<class T> inline auto operator<< (WrappedStream& stream, const T& pFn) -> decltype(pFn(stream)) { return pFn(stream); } //template<class T> //auto operator<< (WrappedStream<std::wostringstream>& stream, const T& pFn) -> decltype(pFn(stream)) //{ // return pFn(stream); //} inline WrappedStream operator<<(Logger& logger, std::function<WrappedStream(Logger&)>& s) { return s(logger); } namespace io { inline WrappedStream endl(Logger& rhs) { return rhs.flush(); } } Logger wrap_cout(std::cout); } Юзать так: test::wrap_cout << "Thread [" << std::this_thread::get_id() << "] put message: elapsed time: " << 1 << test::io::endl; Добавлено Вот в ideone выложил https://ideone.com/1JFO4Z |
Сообщ.
#4
,
|
|
|
Цитата Wound @ inline WrappedStream operator<<(Logger& logger, std::function<WrappedStream(Logger&)>& s) { return s(logger); } Там надо operator<<(WrappedStream<std::wostringstream>(*fn)(Logger&)), или что-то типа того |
Сообщ.
#5
,
|
|
|
Цитата Олег М @ Там надо operator<<(WrappedStream<std::wostringstream>(*fn)(Logger&)), или что-то типа того Да, я пробовал уже. Хоть убей не заходит в глоьальный. Но, если объявить вот такой в классе WrappedStream: template<typename T> WrappedStream operator<<(test::WrappedStream(*)(T& arg)) { return *this; } То в него заходит. Правда не понятно как теперь вызвать эту функцию, она же параметр должна принимать. Добавлено Разве что передавать ссылку на родительский класс внутрь WrappedStream |
Сообщ.
#6
,
|
|
|
Во. Так лучше.
|
Сообщ.
#7
,
|
|
|
Цитата Qraizer @ Во. Так лучше. Да, даже если передать ссылку на базовый класс, то все работает, вызывается манипулятор, все ок, но есть прикол. При вызове flush внутри манипулятора, на этапе создания WrappedStream, летит исключение на захвате мьютекса, потому как судя по всему, он уже захватил этот объект до этого вызова. |
Сообщ.
#8
,
|
|
|
Не, я про дополненный код.
Короче. В итоге там зовётся test::GuardStream::write<test::WrappedStream (*)(test::Logger&)>(), который для std::basic_ostream<> зовёт operator <<() с переданным параметром, но std ничего не знает про перегрузку operator<< для test::WrappedStream (*)(test::Logger&), она знает лишь перегрузку для std::basic_ios<>& (*)(std::basic_ios<>&). Поэтому и срабатывает каст указателя на функцию в void*. |
Сообщ.
#9
,
|
|
|
Цитата Qraizer @ но std ничего не знает про перегрузку operator<< для test::WrappedStream (*)(test::Logger&), она знает лишь перегрузку для std::basic_ios<>& (*)(std::basic_ios<>&) Да, я классу WrappedStream уже сделал делал такой оператор, но в таком случае WrappedStream ничего не знает о Logger Добавлено Передал ему ссылку на Logger, в итоге выхватил исключение при попытке залочить мьютекс. Может быть тут можно как то дизайн пересмотреть, а то у меня голова уже не варит, с 3 ночи сижу пытаюсь этот манипулятор написать. Мне кажется я уже все варианты перепробовал. Добавлено Тут у меня просто была проблема в том, что когда пишешь в лог наподобии: log << param1 << param2 << paramN из нескольких потоков, то сообщения от разных потоков формируются в одно и получается мешанина. Я начал с этим извращаться, вот доизвращался до такого варианта и все работает вроде как, но вот на манипуляторе сел в лужу |
Сообщ.
#10
,
|
|
|
Ну да, перемудрил, пожалуй. Тебе было надо лишь залочить логгер до конца выражения. Для этого достаточно временного Guard, лочащего агрегированный в логгер std::basic_ostream, и отпускающего его в деструкторе, а сами << пусть бы работали сами по себе, стандартные. И не надо было бы никаких враперов.
Добавлено Было бы что-то типа Guard(log) << param1 << param2 << paramN; |
Сообщ.
#11
,
|
|
|
Цитата Qraizer @ а сами << пусть бы работали сами по себе, стандартные. И не надо было бы никаких враперов. Да дело в том, что они у меня не стандартные, это тут в примере используется std::cout, и тут можно обычный std::endl юзнуть, а у меня вместо потока на самом деле стоит std::wostringstream, в него std::endl не выводится. Плюс у меня есть функция flush, которая скидывает данные во внутреннюю очередь. А потом отдельный поток в логе, когда очередь забивается до определенного размера, скидывает ее в файл. Поэтому и пришлось извращаться с нестандартным манипулятором. Добавлено Вернее с std::endl я погарячился немного, я хотел flush вызывать внутри манипулятора, чтобы скидывать во внутренюю очередь сформированную строку. |
Сообщ.
#12
,
|
|
|
Та ну и какая разница? Они все наследуются от std::basic_ios<>, а манипуляторы на него завязаны..
Добавлено Вот, нашёл ссылку. С трудом Маразм программёров (сообщение #3017166) |
Сообщ.
#13
,
|
|
|
Цитата Qraizer @ Вот, нашёл ссылку. С трудом Маразм программёров (сообщение #3017166) Ну так это больше похоже на тестовый вариант что тут выложил выше, он то тоже работает с потоками ostream. Т.е. тут в твоем примере работа идет напрямую с потоком. А в моем случае работа идет напрямую с очередью std::wstring'ов, т.е. пишется оно в буфер, а уж потом из буфера записывается в поток. Цитата Qraizer @ Та ну и какая разница? Они все наследуются от std::basic_ios<>, а манипуляторы на него завязаны.. Так я не знаю где мне вызвать функцию flush ее нужно вызвать после формирования строки в поток. Вот я и думал замаскировать ее под манипулятор. Вот это вот flush: void Logger::write(const std::wstring& string) { std::unique_lock<std::mutex> mIsFlushed(m_mutex_flusher); if (m_main_log.size() >= m_cachesize) { m_bIsQueueReady.store(false); m_flusher.notify_one(); m_cleaner.wait(mIsFlushed, [this] { return m_bIsQueueReady.load(); }); } m_main_log.push(string); } |
Сообщ.
#14
,
|
|
|
Цитата Wound @ Так я не знаю где мне вызвать функцию flush ее нужно вызвать после формирования строки в поток. Ты знаешь какой тип у "io::endl" ? Сделай с аргументом-ссылкой на этот тип процедуру "operator<<". И сделай в этой процедуре вывод CRLF в файл вместе с flush. --- У тебя всё как-то сложно получилось. Можно сделать процедуру записи в лог с переменным числом параметров типа как "printf". |
Сообщ.
#15
,
|
|
|
Цитата ЫукпШ @ Ты знаешь какой тип у "io::endl" ? Это мой манипулятор, я его сам написал. Могу любой тип ему сделать Цитата ЫукпШ @ Сделай с аргументом-ссылкой на этот тип процедуру "operator<<". И сделай в этой процедуре вывод CRLF в файл вместе с flush. Вот, в этом и проблема. Вот код выше в ideone есть, он работает, компилируется и запускается, но вместо вызова io::endl, пишется ее адрес в поток. Адрес пишется, потому что класс, который выводит в поток, ничего не знает о логере, в котором реализован метод flush. Вот в примере выше - я пытался вызвать flush, пока что безуспешно Добавлено Цитата ЫукпШ @ Можно сделать процедуру записи в лог с переменным числом параметров типа как "printf". Можно, у меня так и было, теперь я решил заморочиться на потоках, так удобнее, не нужно задавать форматы и всякие символы - выводишь что хочешь в поток и все. Да согласен что сложно получилось, но по другому я пока не придумал как это реализовать. Добавлено И вообще можно ли такое реализовать? Но ведь стандартный std::endl ведь как то работает, значит ведь можно ведь как то это сделать Унаследоваться и напрямую все перегрузить? |