I have designed a parameter class which allows me to write code like this:
//define parameter
typedef basic_config_param<std::string> name;
void test(config_param param) {
if(param.has<name>()) { //by name
cout << "Your name is: " << param.get<name>() << endl;
}
unsigned long & n = param<ref<unsigned long> >(); //by type
if(param.get<value<bool> >(true)) { //return true if not found
++n;
}
}
unsigned long num = 0;
test(( name("Special :-)"), ref<unsigned long>(num) )); //easy to add a number parameter
cout << "Number is: " << num; //prints 1
The performance of the class is pretty fast: everything is just a reference on the stack. And to save all the information I use an internal buffer of up to 5 arguments before it goes to heap allocation to decrease the size of every single object, but this can be easily changed.
Why isn't this syntax used more often, overloading operator,()
to implement named parameters? Is it because of the potential performance penalty?
One other way is to use the named idiom:
object.name("my name").ref(num); //every object method returns a reference to itself, allow object chaining.
But, for me, overloading operator,()
looks much more "modern" C++, as long you don't forget to uses double parentheses. The performance does not suffer much either, even if it is slower than a normal function, so is it negligible in most cases.
I am probably not the first one to come up with a solution like this, but why isn't it more common? I have never seen anything like the syntax above (my example) before I wrote a class which accepts it, but for me looks it perfect.
My question is why this syntax is not used more, overloading operator,() to implement named parameters.
Because it is counter-intuitive, non-human-readable, and arguably a bad programming practice. Unless you want to sabotage the codebase, avoid doing that.
test(( name("Special :-)"), ref<unsigned long>(num) ));
Let's say I see this code fragment for the first time. My thought process goes like this:
()
.Test
is a class, but test
is a function), and you don't have C
prefixes).name
in the source code, I discover that it is class. And that it overloads the ,
operator, so it actually doesn't discard the first argument anymore. See how much time is wasted here? Frankly, writing something like that can get you into trouble, because you use language features to make your code look like something that is different from what your code actually does (you make a function call with one argument look like it has two arguments or that it is a variadic function). Which is a bad programming practice that is roughly equivalent to overloading operator+ to perform substractions instead of additions.
Now, let's consider a QString example.
QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName);
Let's say I see it for the first time in my life. That's how my thought process goes:
status
of type QString.%1
-style entries and returns QString&. So the chain of .arg()
calls instantly makes sense. Please note that something like QString::arg can be templated, and you'll be able to call it for different argument types without manually specifying the type of argument in <>
.looks very more "modern" C++
"New and shiny" sometimes means "buggy and broken" (slackware linux was built on a somewhat similar idea). It is irrelevant if your code looks modern. It should be human-readable, it should do what it is intended to do, and you should waste the minimum possible amount of time in writing it. I.e. you should (personal recommendation) aim to "implement a maximum amount of functionality in a minimum amount of time at a minimum cost (includes maintenance)", but receive the maximum reward for doing it. Also it makes sense to follow KISS principle.
Your "modern" syntax does not reduce development cost, does not reduce development time, and increases maintenance cost (counter-intuitive). As a result, this syntax should be avoided.