Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 02.11.21 19:58
Оценка:
Здравствуйте!

Изучаю новые стандарты методом тыка и с вашей помощью, коллеги


Такой код:
  Скрытый текст
class myQtLog
{
    QtMsgType                                 m_msgType;
    QString                                   m_str;
    QTextStream                               m_stream;


public:

    myQtLog(QtMsgType msgType = QtInfoMsg) // { QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg }
    : m_msgType(msgType)
    , m_str()
    , m_stream()
    {
        m_stream.setString(&m_str);
    }

    myQtLog()                    = delete;
    myQtLog( const myQtLog &)     = delete;
    myQtLog(myQtLog&&)            = default;
    myQtLog& operator=(const myQtLog &scopeExec) = delete;


    ~myQtLog()
    {
        m_stream.flush();

        switch( m_msgType )
        {
            case QtDebugMsg   : qDebug   ("%s\n", qUtf8Printable(m_str)); break;
            case QtInfoMsg    : qInfo    ("%s\n", qUtf8Printable(m_str)); break;
            case QtWarningMsg : qWarning ("%s\n", qUtf8Printable(m_str)); break;
            case QtCriticalMsg: qCritical("%s\n", qUtf8Printable(m_str)); break;
            case QtFatalMsg   : qFatal   ("%s\n", qUtf8Printable(m_str)); break;
            default: // QtSystemMsg?
                                qInfo    ("[!SYSTEM!] %s\n", qUtf8Printable(m_str)); break;
        }
    }


    myQtLog& operator<<(QChar c)                  { m_stream<<c; return *this; }
    myQtLog& operator<<(char c)                   { m_stream<<c; return *this; }
    myQtLog& operator<<(short i)                  { m_stream<<i; return *this; }
    myQtLog& operator<<(unsigned short i)         { m_stream<<i; return *this; }

    // ...


}; // myQtLog



class myQtLogStream
{
    QtMsgType                                 m_msgType;

    myQtLog mkLog() { return myQtLog(m_msgType); } // { return myQtLog(m_msgType); }

public:

    myQtLogStream(QtMsgType msgType = QtInfoMsg) // { QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg }
    : m_msgType( msgType )
    {}

    myQtLogStream( const myQtLogStream & )           = default;

    myQtLogStream& operator=( const myQtLogStream& ) = default;


    myQtLog operator<<(QChar c)                  { return std::move( mkLog()<<c); }                             // { return mkLog()<<c; }    // !!! attempting to reference a deleted function
    myQtLog operator<<(char c)                   { return std::move( mkLog()<<c); }                             // { return mkLog()<<c; }                        
    myQtLog operator<<(short i)                  { return std::move( mkLog()<<i); }                             // { return mkLog()<<i; }                        
    myQtLog operator<<(unsigned short i)         { return std::move( mkLog()<<i); }                             // { return mkLog()<<i; }                        

}; // class myQtLogStream


//!!! Первая версия
/*
inline myQtLog myQtDebug   () { return myQtLog(QtDebugMsg   ); }
inline myQtLog myQtInfo    () { return myQtLog(QtInfoMsg    ); }
inline myQtLog myQtWarning () { return myQtLog(QtWarningMsg ); }
inline myQtLog myQtCritical() { return myQtLog(QtCriticalMsg); }
inline myQtLog myQtFatal   () { return myQtLog(QtFatalMsg   ); }
*/


extern myQtLogStream tkfDebug   ;
extern myQtLogStream tkfInfo    ;
extern myQtLogStream tkfWarning ;
extern myQtLogStream tkfCritical;
extern myQtLogStream tkfFatal   ;



//----------------------------------------------------------------------------
// lib.cpp

myQtLogStream myQtDebug    = myQtLogStream(QtDebugMsg   );
myQtLogStream myQtInfo     = myQtLogStream(QtInfoMsg    );
myQtLogStream myQtWarning  = myQtLogStream(QtWarningMsg );
myQtLogStream myQtCritical = myQtLogStream(QtCriticalMsg);
myQtLogStream myQtFatal    = myQtLogStream(QtFatalMsg   );



//----------------------------------------------------------------------------
// main.cpp
mtQtInfo << 'Y';



Кутишные логи показались неудобными, и способ записи разнотипных сообщений там отличается

Решил обернуть. Сначала написал myQtLog и использовал inline функции, помеченные "//!!! Первая версия"

Работало.

Решил избавится от скобочек при вызове лого-потока, и сделать глобальные объекты типа std::cout, доколбасил myQtLogStream
Не работает
Маньяк Робокряк колесит по городу
Re: Вопрос про мувы и пр
От: T4r4sB Россия  
Дата: 02.11.21 20:12
Оценка: +1
Здравствуйте, Marty, Вы писали:


M> myQtLog operator<<(QChar c) { return std::move( mkLog()<<c); }

M> myQtLog operator<<(char c) { return std::move( mkLog()<<c); }
M> myQtLog operator<<(short i) { return std::move( mkLog()<<i); }
M> myQtLog operator<<(unsigned short i) { return std::move( mkLog()<<i); }

А почему тут омперсанды забыл?

myQtLog& operator<<(QChar c)
итд

А, я понял, почему. Ты создаёшь объект внутри, а дальше пытаешься из функции, возвращающей ссылку, вернуть этот созданный внутри объект. Да, тут мувы надо расставить.
Отредактировано 02.11.2021 20:14 T4r4sB . Предыдущая версия .
Re[2]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 02.11.21 20:33
Оценка:
Здравствуйте, T4r4sB, Вы писали:


TB>А почему тут омперсанды забыл?


TB>myQtLog& operator<<(QChar c)

TB>итд

TB>А, я понял, почему. Ты создаёшь объект внутри, а дальше пытаешься из функции, возвращающей ссылку, вернуть этот созданный внутри объект. Да, тут мувы надо расставить.


Да, пользуясь уже готовым кодом и его операторами <<, пытаюсь вернуть, а вернее мувнуть, другой объект, у которого есть свои операторы <<, и который в своём деструкторе финализирует запись в логи.

С тем объектом есть проблема, что он тут должен быть только перемещаемым
Маньяк Робокряк колесит по городу
Re[3]: Вопрос про мувы и пр
От: T4r4sB Россия  
Дата: 02.11.21 20:40
Оценка:
Здравствуйте, Marty, Вы писали:

Так тоже должно прокатить на RVO:
myQtLog operator<<(QChar c) { 
  auto result = mkLog();
  result << c;
  return result;
}

Но это длиннее
Re: Вопрос про мувы и пр
От: B0FEE664  
Дата: 03.11.21 13:28
Оценка:
Здравствуйте, Marty, Вы писали:

M>Решил избавится от скобочек при вызове лого-потока, и сделать глобальные объекты типа std::cout, доколбасил myQtLogStream

M>Не работает
Если я правильно понимаю, вот здесь:
myQtLog operator<<(char c) { return std::move( mkLog()<<c); }

создаётся объект myQtLog,
потом он мувится в другой объект myQtLog
затем вызывается деструктор ~myQtLog()
в котором вызываются функции от уже смувленных (пустых) членов класса.
Поэтому не работает.
А в чём вопрос?
И каждый день — без права на ошибку...
Re[4]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 16:19
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Но это длиннее


Не прокатило. В итоге, всё перевёл в MyLogImpl, а MyLog храню только QSharedPointer<MyLogImpl>. Так работает. Но хочется понять, как бы сделать без шаред птр
Маньяк Робокряк колесит по городу
Re[2]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 16:22
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Если я правильно понимаю, вот здесь:

BFE>
BFE>myQtLog operator<<(char c) { return std::move( mkLog()<<c); }
BFE>

BFE>создаётся объект myQtLog,
BFE>потом он мувится в другой объект myQtLog
BFE>затем вызывается деструктор ~myQtLog()

Разве деструктор должен вызываться? мув вроде как раз против этого и придуман

Если так
myQtLog operator<<(char c) { return mkLog()<<c; }


Тоже не работает


BFE>в котором вызываются функции от уже смувленных (пустых) членов класса.

BFE>Поэтому не работает.

Нет, не поэтому


BFE>А в чём вопрос?


Как сделать правильно
Маньяк Робокряк колесит по городу
Re[3]: Вопрос про мувы и пр
От: B0FEE664  
Дата: 03.11.21 17:39
Оценка:
Здравствуйте, Marty, Вы писали:

BFE>>Если я правильно понимаю, вот здесь:

BFE>>
BFE>>myQtLog operator<<(char c) { return std::move( mkLog()<<c); }
BFE>>

BFE>>создаётся объект myQtLog,
BFE>>потом он мувится в другой объект myQtLog
BFE>>затем вызывается деструктор ~myQtLog()

M>Разве деструктор должен вызываться? мув вроде как раз против этого и придуман

Не-а. Он переносит (может переносить) содержимое одного объекта в другой. В некорректной аналогии move — это swap с пустым объектом. Так что для каждого вызванного конструктора должен быть вызван деструктор.

M>Если так

M>
M>myQtLog operator<<(char c) { return mkLog()<<c; }
M>

M>Тоже не работает
И не должно, если я правильно понимаю: в возвращённом объекте, в m_stream, будет хранится указатель на уничтоженную строку.

BFE>>в котором вызываются функции от уже смувленных (пустых) членов класса.

BFE>>Поэтому не работает.
M>Нет, не поэтому
А почему?

BFE>>А в чём вопрос?

M>Как сделать правильно
Это зависит от того, что называть "правильно".
Например, приложение собранное с
DEFINES += QT_NO_DEBUG_OUTPUT
в .pro выкинет весь qDebug() вывод (и некоторые вызовы для этого вывода). Это правильно или нет?
Ещё иногда считают, что использовать не константные глобальные объекты — не правильно. Бывают даже ситуации, когда std::cout используется до создания, что приводит к падению на старте.
Так что вы понимаете под "правильно"?
И каждый день — без права на ошибку...
Отредактировано 03.11.2021 17:41 B0FEE664 . Предыдущая версия .
Re[4]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 19:00
Оценка:
Здравствуйте, B0FEE664, Вы писали:


M>>Разве деструктор должен вызываться? мув вроде как раз против этого и придуман

BFE>Не-а. Он переносит (может переносить) содержимое одного объекта в другой. В некорректной аналогии move — это swap с пустым объектом. Так что для каждого вызванного конструктора должен быть вызван деструктор.

А можно корректную аналогию? А то у меня сложилось впечатление, что move — это "swap" с неинициализированным объектом


M>>Если так

M>>
M>>myQtLog operator<<(char c) { return mkLog()<<c; }
M>>

M>>Тоже не работает
BFE>И не должно, если я правильно понимаю: в возвращённом объекте, в m_stream, будет хранится указатель на уничтоженную строку.

Не работает потому, что не канпиляеца


BFE>>>в котором вызываются функции от уже смувленных (пустых) членов класса.

BFE>>>Поэтому не работает.
M>>Нет, не поэтому
BFE>А почему?

Не канпиляеца


BFE>>>А в чём вопрос?

M>>Как сделать правильно
BFE>Это зависит от того, что называть "правильно".


Смотри. Я хочу завести глобальные объекты myDebug/myInfo/myWarning/etc типа MyLogStream. У MyLogStream есть operator<<, который возвращает экземпляр MyLog, перед этим вызвав его operator<< для переданного аргумента.

В свою очередь, MyLog пишет всё в свой буфер, и в деструкторе финализирует запись и сливает строку в кутишный лог с ссотвествующим левелом. Сейчас я решил вопрос, заведя MyLogImpl, в котором делается реальная работа, а в MyLog храню только QSharedPointer<MyLogImpl>. Теперь CTOR копирования и operator= работают по умолчанию, и ничего мутить не надо. Использование выглядит так:

myDebug << "Test debug" << 1u;
myInfo  << "Test info"  << 2u;


Тут вызывается operator<< для глобального объекта myDebug/myInfo, там создаётся новый MyLog и для него вызывается operator<<. Затем это MyLog возвращается, и последующие operator<< вызываются уже для него. В конце выражения MyLog уничтожается, в деструкторе мембера, который лежит в шаред поинтере, производится запись всего сообщения в кутишный лог с соответсвующим уровнем.

Правильно, в данном случае — это используя мув семантику современных плюсиков, реализовать всё то же, но без использования шаред поинтера


BFE>Например, приложение собранное с

BFE>DEFINES += QT_NO_DEBUG_OUTPUT
BFE>в .pro выкинет весь qDebug() вывод (и некоторые вызовы для этого вывода). Это правильно или нет?

А info/warning/critical/fatal — тоже выкинет? Если да, то это — неправильно.
Но я не понял, к чему сейчас это было


BFE>Ещё иногда считают, что использовать не константные глобальные объекты — не правильно. Бывают даже ситуации, когда std::cout используется до создания, что приводит к падению на старте.

BFE>Так что вы понимаете под "правильно"?

Под "правильно" я понимаю не лезть со своей демагогией, если по существу сказать нечего
Маньяк Робокряк колесит по городу
Re: Вопрос про мувы и пр
От: wander  
Дата: 03.11.21 20:34
Оценка:
Здравствуйте, Marty, Вы писали:

M>Не работает



class myQtLog
{
    
    // ...

    myQtLog&& operator<<(QChar c) &&                  { m_stream << c; return std::move(*this); }
    myQtLog&& operator<<(char c) &&                   { m_stream << c; return std::move(*this); }
    myQtLog&& operator<<(short i) &&                  { m_stream << i; return std::move(*this); }
    myQtLog&& operator<<(unsigned short i) &&         { m_stream << i; return std::move(*this); }

    // ...


}; // myQtLog

// ...

class myQtLogStream
{
    QtMsgType m_msgType;

    myQtLog mkLog() { return myQtLog(m_msgType); }
public:

    myQtLogStream(QtMsgType msgType = QtInfoMsg) // { QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg }
    : m_msgType( msgType )
    {}

    myQtLogStream( const myQtLogStream & )           = default;
    myQtLogStream& operator=( const myQtLogStream& ) = default;


    myQtLog operator<<(QChar c)                  { return mkLog() << c; }
    myQtLog operator<<(char c)                   { return mkLog() << c; }
    myQtLog operator<<(short i)                  { return mkLog() << i; }
    myQtLog operator<<(unsigned short i)         { return mkLog() << i; }

}; // class myQtLogStream
Re[2]: Вопрос про мувы и пр
От: wander  
Дата: 03.11.21 20:52
Оценка:
Здравствуйте, wander, Вы писали:

W>Здравствуйте, Marty, Вы писали:


https://rextester.com/IVRLE36996

  Код по мотивам (без Qt)

enum QtMsgType
{
    QtDebugMsg = 1, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg
};


#include <sstream>
#include <iostream>

class myQtLog
{
    QtMsgType                     m_msgType;
    std::stringstream             m_stream;

public:
    explicit myQtLog(QtMsgType msgType = QtInfoMsg) // { QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg }
        : m_msgType(msgType)
        , m_stream()
    { }

    myQtLog()                    = delete;
    myQtLog( const myQtLog &)     = delete;
    myQtLog(myQtLog&& other)
        : m_msgType(other.m_msgType)
        , m_stream(std::move(other.m_stream))
    {
        other.m_msgType = (QtMsgType)-1;
    }
    myQtLog& operator=(const myQtLog &scopeExec) = delete;

    ~myQtLog()
    {
        if(m_msgType != -1)
        {
            m_stream.flush();

            switch( m_msgType )
            {
                case QtDebugMsg   : std::cout << "Debug: " << m_stream.str() << std::endl; break;
                case QtInfoMsg    : std::cout << "Info: "  << m_stream.str() << std::endl; break;
                case QtWarningMsg : std::cout << "Warn: "  << m_stream.str() << std::endl; break;
                case QtCriticalMsg: std::cout << "Crit: "  << m_stream.str() << std::endl; break;
                case QtFatalMsg   : std::cout << "Fatal: " << m_stream.str() << std::endl; break;
                default:
                       ;
            }
        }
    }

    myQtLog&& operator<<(char c) &&                   { m_stream << c; return std::move(*this); }
    myQtLog&& operator<<(short i) &&                  { m_stream << i; return std::move(*this); }
    myQtLog&& operator<<(unsigned short i) &&         { m_stream << i; return std::move(*this); }

    // ...

}; // myQtLog

class myQtLogStream
{
    QtMsgType                                 m_msgType;

    myQtLog mkLog() { return myQtLog(m_msgType); } // { return myQtLog(m_msgType); }

public:

    myQtLogStream(QtMsgType msgType = QtInfoMsg) // { QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg }
        : m_msgType( msgType )
    {}

    myQtLogStream( const myQtLogStream & )           = default;

    myQtLogStream& operator=( const myQtLogStream& ) = default;

    myQtLog operator<<(char c)                   { return mkLog() << c; }
    myQtLog operator<<(short i)                  { return mkLog() << i; }
    myQtLog operator<<(unsigned short i)         { return mkLog() << i; }

}; // class myQtLogStream

extern myQtLogStream tkfDebug   ;
extern myQtLogStream tkfInfo    ;
extern myQtLogStream tkfWarning ;
extern myQtLogStream tkfCritical;
extern myQtLogStream tkfFatal   ;

//----------------------------------------------------------------------------
// lib.cpp

myQtLogStream myQtDebug    = myQtLogStream(QtDebugMsg   );
myQtLogStream myQtInfo     = myQtLogStream(QtInfoMsg    );
myQtLogStream myQtWarning  = myQtLogStream(QtWarningMsg );
myQtLogStream myQtCritical = myQtLogStream(QtCriticalMsg);
myQtLogStream myQtFatal    = myQtLogStream(QtFatalMsg   );

//----------------------------------------------------------------------------

// main.cpp
int main()
{

    myQtInfo << 'Y';
}
Re[2]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 21:39
Оценка:
Здравствуйте, wander, Вы писали:

W>Здравствуйте, Marty, Вы писали:


M>>Не работает


W>
W>class myQtLogStream
W>{
W>    myQtLogStream( const myQtLogStream & )           = default;
W>    myQtLogStream& operator=( const myQtLogStream& ) = default;
W>}; // class myQtLogStream
W>


Видимо, это главное, что ты хотел сказать?
Пробовал и так
Маньяк Робокряк колесит по городу
Re[3]: Вопрос про мувы и пр
От: wander  
Дата: 03.11.21 22:42
Оценка:
Здравствуйте, Marty, Вы писали:

M>Видимо, это главное, что ты хотел сказать?

M>Пробовал и так

Нет. Соседний пост смотри.
Re[3]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 23:00
Оценка:
Здравствуйте, wander, Вы писали:

W>https://rextester.com/IVRLE36996


Спасибо, идею вроде понял.

Но хранить состояние валидности в чужом enum'е — тоже не самая лучшая идея
Маньяк Робокряк колесит по городу
Re[4]: Вопрос про мувы и пр
От: wander  
Дата: 03.11.21 23:14
Оценка:
Здравствуйте, Marty, Вы писали:

M>Но хранить состояние валидности в чужом enum'е — тоже не самая лучшая идея


Идея не в этом.
Идея в том, что хранить состояние. А в чем именно — это уже твое дело.
Я просто малой кровью твой код исправил и все. Нужно это понимать.
Re[5]: Вопрос про мувы и пр
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.21 23:34
Оценка:
Здравствуйте, wander, Вы писали:

M>>Но хранить состояние валидности в чужом enum'е — тоже не самая лучшая идея


W>Идея не в этом.


Я понял


W>Идея в том, что хранить состояние. А в чем именно — это уже твое дело.

W>Я просто малой кровью твой код исправил и все. Нужно это понимать.

Я понял. Но такие примеры — плохие примеры, сорян за тавтологию
Маньяк Робокряк колесит по городу
Re[6]: Вопрос про мувы и пр
От: wander  
Дата: 04.11.21 12:57
Оценка: +1
Здравствуйте, Marty, Вы писали:

M>Я понял. Но такие примеры — плохие примеры, сорян за тавтологию


Дарёному коню в зубы не смотрят.
Re: Вопрос про мувы и пр
От: rg45 СССР  
Дата: 11.11.21 18:42
Оценка:
M>
M>    myQtLog operator<<(QChar c)                  { return std::move( mkLog()<<c); }    // { return mkLog()<<c; }    // !!! attempting to reference a deleted function
M>    myQtLog operator<<(char c)                   { return std::move( mkLog()<<c); }   // { return mkLog()<<c; }                        
M>    myQtLog operator<<(short i)                  { return std::move( mkLog()<<i); }   // { return mkLog()<<i; }                        
M>    myQtLog operator<<(unsigned short i)         { return std::move( mkLog()<<i); }   // { return mkLog()<<i; }                        
M>


Вот тут есть один интересный нюансик. В твоей первой версии у тебя под return-ами стояли prvalue выражения и на них распространялись требования Mandatory copy/move elision. Такие return-ы благополучно компилируются даже при отсутсвии copy- move- конструктов в типах возвращаемых значений. Как только ты добавил std:move, выражения под return-ами стали относиться к категории xvalue, на которые уже не распространяется требование mandatory copy/move elision. Компиляторам хоть и разрешается применять оптимизации RVO/NRVO для таких вырахений, но наличие copy или move конструктора в типе возвращаемого значения является обязательным.
--
Re: Вопрос про мувы и пр
От: qaz77  
Дата: 22.11.21 15:56
Оценка: 2 (1)
Здравствуйте, Marty, Вы писали:
M>Изучаю новые стандарты методом тыка и с вашей помощью, коллеги

Вот здесь мне очень понравилось изложение про && и std::move
Базовый курс C++ (MIPT, ILab). Lecture 5. RAII и перемещение

Мне бы кто-нибудь 8 лет назад все так подробно разжевал...
Отредактировано 22.11.2021 15:57 qaz77 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.