Let's overload the semicolon!

637 views
Skip to first unread message

Derek Ross

unread,
Jul 15, 2016, 10:27:24 PM7/15/16
to ISO C++ Standard - Future Proposals
Maybe I should have waited until April 1st before posting this... but is this a good idea? Seems to be OK to me.  If something like this existed decades ago, I would have used it many many times by now (unless there's some "gotcha" I missed)...

Let's put a new operator into C++: the "terminator operator." It would look like this: ';' (a semicolon). It would be a unary postfix operator with a lower precedence than the comma operator.

A class can overload the terminator operator by defining a member function like:

 void operator;(){
   
..etc..
 
}

The purpose of the terminator operator is to perform some action when a sequence of operations reaches the statement terminator. If a terminator operator isn't defined for an object, then nothing special happens.

One application would be automatic buffer flushing for a logging function. For example, a conventional call to the C function syslog:

 syslog(LOG_ERR, "The value is %d", value);
 syslog
(LOG_INFO, "The device is %s", dev);
 
could be replaced with a C++ Syslog object along with overloaded operators:

 Syslog << Log_Err << "The value is " << value ;
 
Syslog << Log_Info << "The device is " << dev ;

The CSyslog class would use the terminator operator to trigger the transmission of the buffered data to the syslog daemon (probably through an internal call to ::syslog().)
 
Below is a slightly more fleshed-out example, of a cout-styled facility that automatically appends a newline at the end of the statement, like Java's "println" function.


struct CoutLn
{        
   
// Typical inserter function.
   
CoutLn& operator<<(const char* x)
   
{
        cout
<< x;
       
return *this;
   
}
   
   
// Terminator operator.
   
// Send a newline to the console at
   
// statement termination.
   
void  operator;()
   
{
        cout
<< '\n';        
   
}
   
   
// Manipulator to ignore the next
   
// terminator.
   
const static struct SkipNl {} skipnl;
   
   
// Return void on SkipNl, thus
   
// "deactivating" the subsequent
   
// terminator operator.
   
void operator<<(const SkipNl){}
   
} coutln;

int main()
{    
    coutln
<< "A" ;                   // Prints "A\n"
    coutln
<< "B" << CoutLn::skipnl ; // Prints "B"    
}



D. B.

unread,
Jul 16, 2016, 5:04:46 AM7/16/16
to std-pr...@isocpp.org
When one could trivially make a tag struct to perform the same purpose (e.g. std::endl), overloading the statement delimiter to save typing a few characters looks absurd to me
 - born of overinterpretation of DRY, potentially very dangerous, and doesn't explain how to handle situations where you want to output a bit now, do some more calculation, then output the endl/flush/whatever later. What, would you prefer to write 2 classes for every situation, one with the semicolon overloaded and one without?

What's wrong with
Syslog << Log_Err << "The value is " << value << SLgo; // or any other tag

and really now:

    coutln
<< "A" ;                   // Prints "A\n"
    coutln
<< "B" << CoutLn::skipnl ; // Prints "B"   
That's more typing than one line with std::endl and one without, but the latter isn't completely inverse (perverse?) to intuition.

The real solution is to use tag structs OR a non-inserting operator/call (that processes a full 'statement' of output at a time a la printf). Neither of which semantically break the language for the sake of saving a few keystrokes in the few situations to which they're barely applicavble.

Wil Evers

unread,
Jul 16, 2016, 2:12:50 PM7/16/16
to ISO C++ Standard - Future Proposals


On Saturday, July 16, 2016 at 4:27:24 AM UTC+2, Derek Ross wrote:
Maybe I should have waited until April 1st before posting this... but is this a good idea? Seems to be OK to me.  If something like this existed decades ago, I would have used it many many times by now (unless there's some "gotcha" I missed)...

Let's put a new operator into C++: the "terminator operator." It would look like this: ';' (a semicolon). It would be a unary postfix operator with a lower precedence than the comma operator.

A class can overload the terminator operator by defining a member function like:

 void operator;(){
   
..etc..
 
}

The purpose of the terminator operator is to perform some action when a sequence of operations reaches the statement terminator. If a terminator operator isn't defined for an object, then nothing special happens.

C++ already has this; the destructors of temporaries are called at the 'end of full expression', that is, the semicolon.

Wil
 
 

Vishal Oza

unread,
Jul 16, 2016, 3:01:19 PM7/16/16
to ISO C++ Standard - Future Proposals
I also think overloading the semicolon operator is a bad idea as it could lead to breaking changes in you entire code base as well as confusing code. However, I could see a value in static analyzer.  I think a better solution might be a either have expression types-traits or introduce custom operator definition where you define first the the operator, the syntax then optionally the level of precedence, and the then operator overload most common C++ operators would be exempt from this definition. This should also by uncommon looking like C++ lamba notations so it does not break existing code.

Derek Ross

unread,
Jul 16, 2016, 4:01:45 PM7/16/16
to ISO C++ Standard - Future Proposals
Thanks for the insight! It works as expected (see code below). I hereby retract my request for the semicolon operator!

Cheers,
Derek

#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;

struct Syslog
{
   
int level;
    ostringstream o
;
   
   
Syslog(int lev):level(lev){}
   
   
template<class T>
   
Syslog& operator<<(const T& x){
        o
<< x;
       
return *this;
   
}
   
   
~Syslog(){
        cout
<< "Calling ::syslog(" << level << ", \"" << o.str() << "\")" << endl;
   
}
   
   
operator bool (){
       
return bool(o);
   
}
};

int main(){
   
Syslog(1) << "The number is 0x" << hex << 255;
   
Syslog(2) << "The temp is " << 99.99;
   
   
if(Syslog(3) << "Three")
   
{
       
Syslog(4) << "Four";
   
}
   
else
   
{
       
Syslog(5) << "Five";
   
}    
}


/* Prints out:

Calling ::syslog(1, "The number is 0xff")
Calling ::syslog(2, "The temp is 99.99")
Calling ::syslog(3, "Three")
Calling ::syslog(4, "Four")

*/

Tony V E

unread,
Jul 16, 2016, 6:32:51 PM7/16/16
to ISO C++ Standard - Future Proposals
Take a look at how Qt does it with QDebug etc. 

Sent from my BlackBerry portable Babbage Device
From: Derek Ross
Sent: Saturday, July 16, 2016 4:01 PM
To: ISO C++ Standard - Future Proposals
Subject: [std-proposals] Re: Let's overload the semicolon!

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1669dfd0-705b-481b-b740-5f56ebfe19f6%40isocpp.org.

szollos...@gmail.com

unread,
Jul 17, 2016, 1:07:27 PM7/17/16
to ISO C++ Standard - Future Proposals
Re: 'C++ already has this; the destructors of temporaries are called at the 'end of full expression', that is, the semicolon.'

Not when the temporary is bound to a const ref.

Thiago Macieira

unread,
Jul 17, 2016, 1:13:19 PM7/17/16
to std-pr...@isocpp.org
Which is when the developer wants to prevent the destructor from running.

For example, QDebug automatically prints a newline, so you'll find code like:

{
QDebug s = qDebug().nospace();
s << "SomeClass(";
if (condition)
s << "condition";
s << ')';
} // newline printed here

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Reply all
Reply to author
Forward
0 new messages