Why are more commonly used parameters not called?

I developed a parameter class that allows me to write code as follows:

//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 

Class performance is pretty fast: all this is just a stack reference. And to save all the information, I use an internal buffer of up to 5 arguments before it proceeds to allocate heaps to reduce the size of each individual object, but this can be easily changed.

Why is this syntax not used more often, overloading operator,() to implement named parameters? Is this related to potential performance degradation?

Another way is to use a named idiom:

 object.name("my name").ref(num); //every object method returns a reference to itself, allow object chaining. 

But for me, operator,() overloading looks a lot more “modern” C ++, since you will not forget to use double parentheses for a long time. Performance also does not suffer much, even if it is slower than normal, so in most cases it is insignificant.

I'm probably not the first to come up with such a solution, but why is this not common? I have never seen anything like the syntax above (my example) before writing a class that accepts it, but to me it looks perfect.

+7
source share
2 answers

My question is why this syntax is no longer used, overloading operator, () to implement named parameters.

Because it is counterintuitive, unreadable by the person and, possibly, bad programming practice. If you do not want to sabotage the code base, do not do this.

 test(( name("Special :-)"), ref<unsigned long>(num) )); 

Let's say I see this piece of code for the first time. My thinking process is as follows:

  • At first glance, this looks like the “most annoying parsing” since you use double brackets. Therefore, I assume that the test is a variable, and you need to wonder if you forgot to write the type of the variable. Then it occurs to me that this thing really compiles. After that, I have to wonder if this is an instance of the immediately destroyed type test class, and you use lowercase names for all class types.
  • Then I find that it is a function call. Fine.
  • Now the code fragment looks like a function call with two arguments.
  • Now it becomes obvious to me that this cannot be a function call with two arguments, because you used double parentheses.
  • So, NOW I need to understand what is going on in () .
  • I remember that there is a comma operator (which I have never seen in real C ++ code in the last 5 years) that discards the previous argument. So, I have to wonder what is a useful side effect of the name () and that the name () is a function call or type (because you are not using uppercase or lowercase letters to distinguish between a class / function (i.e. Test is a class, but Test is a function), and you have no C ) prefixes.
  • After searching for name in the source code, I found that it is a class. And that it overloads the operator , therefore, in fact, it no longer cancels the first argument.

See how much time wasted here? Honestly, writing something like this can be troublesome because you use language functions to make your code look something different from what your code actually does (you call a function call with one argument, it looks like it has two arguments or that it is a variational function). This is bad programming practice, which is roughly equivalent to overloading the + operator to execute add-ons instead of add-ons.

Now consider the QString example.

  QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName); 

Say I see this for the first time in my life. This is like my thought process:

  • There is a status variable of type QString.
  • It is initialized from a temporary variable of type QString ().
  • ... after calling the QString :: arg method. (I know this is a method).
  • I am watching . arg in the documentation to find out what it does, and find that it replaces the elements %1 -style and returns QString &. So, chaining .arg() calls instantly makes sense. Note that something like QString :: arg may be a template, and you can call it for different types of arguments without specifying the type of the argument in <> .
  • Now this piece of code makes sense, so I'm moving on to another piece.

looks more "modern" C ++

“New and shiny” sometimes means “buggy and broken” (linux for slackware was built on a somewhat similar idea). It doesn’t matter if your code looks modern. It must be human readable, it must do what it intends to do, and you must spend a minimal amount of time on it. That is, you should (personal recommendation) strive to "realize maximum functionality for a minimum period of time at minimum cost (including maintenance)", but get the maximum reward for this. It also makes sense to follow the KISS principle.

Your "modern" syntax does not reduce development costs, does not reduce development time and increases the cost of maintenance (counter-intuitive). As a result of this syntax should be avoided.

+51
source

Not necessary. Your dynamic dispatch (behave differently, depending on the logical type of the argument) can be implemented a) much simpler and b) much faster using specialized specialization.

And if you really need a distinction based on information available only at runtime, I would try moving your test function as a virtual method like param and just use dynamic binding (what it is for and what you are kind of inventing).

The only cases where this approach would be more useful might be multicast scenarios where you want to reduce the code and you can find some similarity patterns.

+3
source

All Articles