Search code examples
c++qtvariadic-templatesvariadic-functionsvariadic-macros

How to do a variadic macro and or template and or function that captures the expression as well as its evaluation?


I have overloaded a class with various << operators

    inline QString operator<<(bool boolean) {
        return (boolean ? QString("true") : QString("false"));
    }
    inline QString operator<<(char const *string) {
        return QString(string);
    }
    inline QString operator<<(int number)                                       {
        return QString::number(number);
    }

Basically what I want to do, is write a debug output that captures the expression, like so:

#define DEBUG(...)
DEBUG(QString("foobar"), someObject.toInt());
// Expression 1: """QString("foobar")"""
// Expression 2: """someObject.toInt()"""

And combines it with its evaluation:

#define DEBUG(...)
DEBUG(QString("foobar"), someObject.toInt());
// Evaluation 1: "foobar"
// Evaluation 2: "1234"

Prepending all with

__FILE__;
QString::number(__LINE__);
__PRETTY_FUNCTION__;

And it should output something like this:

#define DEBUG(...)
DEBUG(QString("foobar"), someObject.toInt());
///////////////// Output /////////////////
File: /home/akiva/Programming/MyApp/source.cpp
Line: 123
Func: void doSomething();

QString("foobar")
"foobar"

someObject.toInt()
"1234"

I am having trouble doing this, as doing recursive variadic macros is not exactly legal. I am also having some additional difficulty, as my previous method of using variadic templates coupled with std::forward, is not working in web assembly as far as I can tell. Ideally the solution once tested will also be compatible with Wasm.

Thanks.


Solution

  • I figured it out by building a small parser to parse the initial expression. The parser is bound to have bugs, as I just pieced it together over an hour.

    Inside a class:

    static void _Continue(QStringList &sl)
    {
        QString s;
        {
            s.append(sl.takeFirst()); // File
            s.append(":");
            s.append(sl.takeFirst()); // Line
            s.append("<br>  ");       // Outputting to a text box.
            s.append(sl.takeFirst()); // Func
        }
    
        /* Parse Expression */
        QStringList args;
        {
            QString expr(sl.takeFirst()); // Expression
            bool isQuote  (false); // ""
            bool isLiteral(false); // ''
            bool isPointer(false); // ->
            int roundLevel(0);   // ()
            int curlyLevel(0);   // {}
            int squarLevel(0);   // []
            int angleLevel(0);   // < > // Fixme handle <<, >>, <=  etc
            expr.remove(QRegularExpression("[/][*].*[*][/]")); // Remove Comments
    
            QString arg;
            QRegularExpression re_CommaWhitespaceAtBeginning("^[, \t\n]*");
            while (!expr.isEmpty()) {
                if (isPointer && expr.front().unicode() != u'>') { isPointer = false; }
                switch (expr.front().unicode()) {
                case u'-' : {
                    isPointer = true; // potentially
                    break;
                }
                case u'"' : {
                    isQuote = !isQuote;
                    break;
                }
                case u'\'': {
                    isLiteral = !isLiteral;
                    break;
                }
                case u'(' : {
                    if (isQuote) { break; }
                    roundLevel++;
                    break;
                }
                case u')' : {
                    if (isQuote) { break; }
                    roundLevel--;
                    break;
                }
                case u'[' : {
                    if (isQuote) { break; }
                    squarLevel++;
                    break;
                }
                case u']' : {
                    if (isQuote) { break; }
                    squarLevel--;
                    break;
                }
                case u'{' : {
                    if (isQuote) { break; }
                    curlyLevel++;
                    break;
                }
                case u'}' : {
                    if (isQuote) { break; }
                    curlyLevel--;
                    break;
                }
                case u'<' : {
                    if (isQuote) { break; }
                    angleLevel++;
                    break;
                }
                case u'>' : {
                    if (isQuote || isPointer) {
                        isPointer = false;
                        break;
                    }
                    angleLevel--;
                    break;
                }
                case u'\\': {
                    if (isQuote) { arg.append(expr.front()); }
                    break;
                }
                case u',' : {
                    if (isQuote
                        || isLiteral
                        || roundLevel
                        || curlyLevel
                        || squarLevel
                        || angleLevel) { break; }
                    args << arg.remove(re_CommaWhitespaceAtBeginning);
                    arg.clear();
                    break;
                }
                default   : {
    
                }
                }
                arg.append(expr.front());
                expr.remove(0,1);
            }
            args << arg.remove(re_CommaWhitespaceAtBeginning);
        }
    
        // sl should now have only values;
        // args should be shorter than sl, even with error
        for (int i = 0; !args.isEmpty(); i++) {
            sl.replace(i, args.takeFirst() + "<br/>" + sl.at(i));
        }
        sl.prepend(s);
    
        /* Messages stored on a map, signal sent to update it. */
        MainWindow::s_StdoutMap.append(sl.join("<br/><br/>"));
        emit MainWindow::s_MW->on_actionUpdate_triggered();
    }
    template<typename type, typename ... T>
    static void _Continue(QStringList &sl, type t, T ... args)
    {
        sl << (*MainWindow::s_MW << t); // overloaded << operator to handle T
        MainWindow::_Continue(sl, args ...);
    }
    template<typename ... T>
    static void _Start(QString file, int line, QString func, QString expr, T ... args)
    {
        QStringList sl;
        {
            sl << file;
            sl << QString::number(line);
            sl << func;
            sl << expr;
        }
        MainWindow::_Continue(sl, args ...);
    }
    #define ct_Debug(...)MainWindow::_Start(__FILE__, __LINE__, __PRETTY_FUNCTION__,\
     #__VA_ARGS__, __VA_ARGS__)
    

    And it gives me an output along the lines of this: enter image description here If you are wondering why it isnt in the console, as said, this is for web assembly.