На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: JoeUser, Qraizer, Hsilgos
Страницы: (4) [1] 2 3 ... Последняя » все  ( Перейти к последнему сообщению )  
> Трабла с манипулятором, Вызывается не то и не там :(
Всем привет.

В общем есть лог файл, хочу выводить в него инфу как в потоки.
Все нормально и отлично, но из за специфики дизайна, что то встал на написании манипулятора.
Я умом понимаю что он хочет, и что нужно сделать, но не пойму как.
По задумке я хочу писать вот так:
ExpandedWrap disabled
    Logger log;
    log << "param1" << 1 << "ololo" << io::endl;

И оно все пишет и работает, но не вызывается манипулятор io::endl;
Он нужен для того, чтобы сбросить содержимое внутреннего потока в файл.
Если например переписать вот так:
ExpandedWrap disabled
    Logger log;
    log << "param1" << 1 << "ololo" << L'\n';
    log.flush();

То все работает просто великолепно, как и задумывалось. Но это же нужно писать вырвиглазный L'\n' + log.flush делать.
Конкретно эти две строчки я хотел запихнуть в io::endl манипулятор, и мне это почти удалось.
Но есть одна загвоздка, не могу понять как перегрузить << для WrappedStream, чтоб он вызвал нужную мне версию.
У меня при любом раскладе вызывается в logger << вот это -> из класса Logger
ExpandedWrap disabled
                    template<class T>
            WrappedStream<std::wostringstream> operator<<(const T& arg)
            {
                return WrappedStream<std::wostringstream>(m_stream, m_streammtx) << arg;
            }

А при последующих вызовах, вызывается << у WrappedStream
ExpandedWrap disabled
    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, вместо того, чтобы она выполнялась.
Где я ступил?
Спасибо, код прилагаю

ExpandedWrap disabled
        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;
            }
Сообщение отредактировано: Wound -
Ты уверен, что код полный?
Одни с годами умнеют, другие становятся старше.
Цитата Qraizer @
Ты уверен, что код полный?

Не, он обрезанный.
Тут реализация всех функций/методов - которые участвуют в формировании лога.
Щас попробую Сделать работоспособным этот кусок.

Добавлено
Ну вот допилил до тестового примера:
ExpandedWrap disabled
    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);
    }

Юзать так:
ExpandedWrap disabled
    test::wrap_cout << "Thread [" << std::this_thread::get_id() << "] put message: elapsed time: " << 1 << test::io::endl;


Добавлено
Вот в ideone выложил https://ideone.com/1JFO4Z
Цитата Wound @
    inline WrappedStream operator<<(Logger& logger, std::function<WrappedStream(Logger&)>& s)
    {
        return s(logger);
    }
 

Там надо operator<<(WrappedStream<std::wostringstream>(*fn)(Logger&)), или что-то типа того
Цитата Олег М @
Там надо operator<<(WrappedStream<std::wostringstream>(*fn)(Logger&)), или что-то типа того

Да, я пробовал уже. Хоть убей не заходит в глоьальный.
Но, если объявить вот такой в классе WrappedStream:
ExpandedWrap disabled
            template<typename T>
            WrappedStream operator<<(test::WrappedStream(*)(T& arg))
            {
                return *this;
            }

То в него заходит. Правда не понятно как теперь вызвать эту функцию, она же параметр должна принимать.

Добавлено
Разве что передавать ссылку на родительский класс внутрь WrappedStream
Во. Так лучше.
Одни с годами умнеют, другие становятся старше.
Цитата Qraizer @
Во. Так лучше.

Да, даже если передать ссылку на базовый класс, то все работает, вызывается манипулятор, все ок, но есть прикол. При вызове flush внутри манипулятора, на этапе создания WrappedStream, летит исключение на захвате мьютекса, потому как судя по всему, он уже захватил этот объект до этого вызова.
Не, я про дополненный код.
Короче. В итоге там зовётся test::GuardStream::write<test::WrappedStream (*)(test::Logger&)>(), который для std::basic_ostream<> зовёт operator <<() с переданным параметром, но std ничего не знает про перегрузку operator<< для test::WrappedStream (*)(test::Logger&), она знает лишь перегрузку для std::basic_ios<>& (*)(std::basic_ios<>&). Поэтому и срабатывает каст указателя на функцию в void*.
Одни с годами умнеют, другие становятся старше.
Цитата Qraizer @
но std ничего не знает про перегрузку operator<< для test::WrappedStream (*)(test::Logger&), она знает лишь перегрузку для std::basic_ios<>& (*)(std::basic_ios<>&)

Да, я классу WrappedStream уже сделал делал такой оператор, но в таком случае WrappedStream ничего не знает о Logger :(

Добавлено
Передал ему ссылку на Logger, в итоге выхватил исключение при попытке залочить мьютекс. Может быть тут можно как то дизайн пересмотреть, а то у меня голова уже не варит, с 3 ночи сижу пытаюсь этот манипулятор написать. Мне кажется я уже все варианты перепробовал.

Добавлено
Тут у меня просто была проблема в том, что когда пишешь в лог наподобии:
log << param1 << param2 << paramN
из нескольких потоков, то сообщения от разных потоков формируются в одно и получается мешанина.
Я начал с этим извращаться, вот доизвращался до такого варианта и все работает вроде как, но вот на манипуляторе сел в лужу :-?
Ну да, перемудрил, пожалуй. Тебе было надо лишь залочить логгер до конца выражения. Для этого достаточно временного Guard, лочащего агрегированный в логгер std::basic_ostream, и отпускающего его в деструкторе, а сами << пусть бы работали сами по себе, стандартные. И не надо было бы никаких враперов.

Добавлено
Было бы что-то типа
ExpandedWrap disabled
    Guard(log) << param1 << param2 << paramN;
Одно но: без Guard вся система рушится, так что все операции с log должны начинаться с этого самого Guard(log).
Одни с годами умнеют, другие становятся старше.
Цитата Qraizer @
а сами << пусть бы работали сами по себе, стандартные. И не надо было бы никаких враперов.

Да дело в том, что они у меня не стандартные, это тут в примере используется std::cout, и тут можно обычный std::endl юзнуть, а у меня вместо потока на самом деле стоит std::wostringstream, в него std::endl не выводится. Плюс у меня есть функция flush, которая скидывает данные во внутреннюю очередь. А потом отдельный поток в логе, когда очередь забивается до определенного размера, скидывает ее в файл.
Поэтому и пришлось извращаться с нестандартным манипулятором.

Добавлено
Вернее с std::endl я погарячился немного, я хотел flush вызывать внутри манипулятора, чтобы скидывать во внутренюю очередь сформированную строку.
Та ну и какая разница? Они все наследуются от std::basic_ios<>, а манипуляторы на него завязаны..

Добавлено
Вот, нашёл ссылку. С трудом
Маразм программёров (сообщение #3017166)
Одни с годами умнеют, другие становятся старше.
Цитата Qraizer @
Вот, нашёл ссылку. С трудом
Маразм программёров (сообщение #3017166)

Ну так это больше похоже на тестовый вариант что тут выложил выше, он то тоже работает с потоками ostream. Т.е. тут в твоем примере работа идет напрямую с потоком. А в моем случае работа идет напрямую с очередью std::wstring'ов, т.е. пишется оно в буфер, а уж потом из буфера записывается в поток.


Цитата Qraizer @
Та ну и какая разница? Они все наследуются от std::basic_ios<>, а манипуляторы на него завязаны..

Так я не знаю где мне вызвать функцию flush :-? ее нужно вызвать после формирования строки в поток. Вот я и думал замаскировать ее под манипулятор.
Вот это вот flush:
ExpandedWrap disabled
        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);
        }
Цитата Wound @
Так я не знаю где мне вызвать функцию flush :-? ее нужно вызвать после формирования строки в поток.

Ты знаешь какой тип у "io::endl" ?
Сделай с аргументом-ссылкой на этот тип процедуру "operator<<".
И сделай в этой процедуре вывод CRLF в файл вместе с flush.
---
У тебя всё как-то сложно получилось.
Можно сделать процедуру записи в лог с переменным числом параметров типа как "printf".
Сообщение отредактировано: ЫукпШ -
Подпись была выключена в связи с наложенным заземлением.
Цитата ЫукпШ @
Ты знаешь какой тип у "io::endl" ?

Это мой манипулятор, я его сам написал. Могу любой тип ему сделать :)

Цитата ЫукпШ @
Сделай с аргументом-ссылкой на этот тип процедуру "operator<<".
И сделай в этой процедуре вывод CRLF в файл вместе с flush.

Вот, в этом и проблема. Вот код выше в ideone есть, он работает, компилируется и запускается, но вместо вызова io::endl, пишется ее адрес в поток. Адрес пишется, потому что класс, который выводит в поток, ничего не знает о логере, в котором реализован метод flush. Вот в примере выше - я пытался вызвать flush, пока что безуспешно :-?

Добавлено
Цитата ЫукпШ @
Можно сделать процедуру записи в лог с переменным числом параметров типа как "printf".

Можно, у меня так и было, теперь я решил заморочиться на потоках, так удобнее, не нужно задавать форматы и всякие символы - выводишь что хочешь в поток и все.
Да согласен что сложно получилось, но по другому я пока не придумал как это реализовать.

Добавлено
И вообще можно ли такое реализовать? Но ведь стандартный std::endl ведь как то работает, значит ведь можно ведь как то это сделать :scratch:
Унаследоваться и напрямую все перегрузить?
1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
0 пользователей:


Рейтинг@Mail.ru
[ Script Execution time: 0,1700 ]   [ 20 queries used ]   [ Generated: 17.07.18, 04:09 GMT ]