What is an ignored qualifier? Well, in a class, lets have a student class:
#include <string>
class Student
{
public:
const std::string Name;
private:
bool m_Present;
public:
Student(const std::string& p_Name)
:
Name(p_Name),
m_Present
{
}
void SetPresent() { m_Present = true; }
void SetAbsent() { m_Present = false; }
};
Now, we may want to access the present flag, therefore provide a function to do so:
bool Present() const { return m_Present }
This function is telling the user of our class quite a bit, it tells them that the return type is boolean and that the calling of this function makes no changes to the class contents (const trailing the parameter list).
However, for me, this isn't quite right, I believe that we want to inform any user that the boolean returned is also constant, it does not change unless you alter the internal value with the "Set" functions, therefore I prefer and like to see code stating:
const bool Present() const { return m_Present; }
This is perhaps overkill and most of the time completely acceptable code to present, however, some might prefer not to see it, specifically anyone defining "-Wignored-qualifiers" as with the "const bool" the const here is technically superfluous, the return type is boolean and a new instance of it, it is not a reference to the internal value, if it were the function may look something more like this:
const bool& Present() const { return m_Present; }
Now we are intrinsically returning a reference to the internal boolean, or even:
const bool* const Present() const { return &m_Present; }
For speed of operation we may directly drive the reference back as a constant boolean constant pointer.
I find this much more informative to the user, they know our intent, the code though more verbose communicates its meaning much more clearly.
As ever, yes I have seen questions asked of interfaces where "bool X()", or "bool X() const" is provided but then programmers have asked "How do I change X", with the const return, ignored qualifier or not, the know not to ask this function to change X and can look up elsewhere in your API.
A blog about my rantings, including Games, Game Development, Gaming, Consoles, PC Gaming, Role Playing Games, People, Gaming tips & cheats, Game Programming and a plethora of other stuff.
Showing posts with label cpp. Show all posts
Showing posts with label cpp. Show all posts
Thursday, 2 November 2017
Wednesday, 19 March 2014
Why I don't like Lambda's (C++)
Why I don't like Lambda's (C++)
I think I'm going to be in the minority of programmers when I say that I don't like Lambda's, in case you don't know, a Lambda is a piece of code you an run inline, so you can create it and run it where you're working... like this:
#include <iostream>
int main ()
{
int sum10 = []
{
int i = 0;
for (int k = 0; k < 10; ++k)
{
i += k;
}
return i;
}();
}
When the code here is compiled & run, the value entered into "sum10" is the result of the function which follows, traditionally we'd write this code like this:
#include <iostream>
int Sum10Function ();
int main ()
{
int sum10 = Sum10Function();
}
int Sum10Function ()
{
int i = 0;
for (int l = 0; k < 10; ++k)
{
i += k;
}
return i;
}
You can see this "Sum10Function" is actually a function, it has a declaration and a definition. And this is how I'd always prefer to do work with functions, I do not like Lambda's because they ignore the greatest power of functions like "Sum10Function".
And that power is having a known location, a location you can find and a location which one can easily find all references to, you know if you have a problem in the function that you have one body to find and you can find it easily. Finding a Lambda, written inline, in a mass of source is not a boon, it is in fact a major headache.
Other authors on the topic, such as "Alex Allain" (http://www.cprogramming.com/c++11/c++11-lambda-closures.html) actually have the balls to intimate that the fact you can write the function inline like the lambda, without having to stop and go create a prototype and a body declaration is a strength, but he; like many others; totally ignores the elephant in the room and that is the maintainability of code.
Alex's other examples on that page also start to use function pointer or functor examples, the first "AddressBook" class example he gives is taking a type, and yes we can assign a lambda to a type "auto"... but we can also assign a function pointer to an "auto", so there's no difference in using a function pointer or using a lambda in that example, except the Lambda falls foul of my point, it's harder to find and manage the source for the function.
The article linked there is also one of the major references if one googles for Lambda's in C++. And it has code examples, which work, but which he describes as "pretty good looking", but which I see as horrible... here's the examples, cleaned up in the same program:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
using namespace std;
vector<int> v;
v.push_back(1);
v.push_back(2);
//...
for (auto itr = v.begin(), end = v.end(); itr != end; itr++)
{
cout << *itr << endl;
}
vector<int> vw;
vw.push_back(1);
vw.push_back(2);
//...
for_each(vw.begin(), vw.end(), [](int val)
{
cout << val << endl;
});
return 1;
}
They both do the same thing, they output the vector, but I don't think the second code looks better, I in fact think both examples need cleaning up, because to my eye they're miss leading, but my argument about the maintainability of Lambda's aside the second example does hint at the only use for them. It hints that they can take the loop variable "val" as an input from the left, i.e. from the iteration over the vector in the "for_each" loop, rather than need to declare that iterator as in the first loop "auto itr = v.begin()"...
But I would still argue that this ease of throwing the code in there, leads to hard to maintain code, I think the first example cleaned up would leave a much more wholesome and maintainable code base...
#include <iostream>
#include <vector>
int main()
{
using namespace std;
vector<int> v = { 1, 2 };
for (auto itr = v.begin(); itr != v.end; ++itr)
{
cout << (*itr) << endl;
}
}
And there is my preferred implementation of that example, I find it simpler and easier to understand, and as you can see I'm using the C++11 declaration of the vector contents to ease understanding, so I'm not throwing an anti C++11 rant here, far from it, I've looked at Lambda's in Python and C# as well as here in C++ and I really don't like the syntax maintenance questions they bring up.
Sometimes they look and can behave very powerfully, but even with the for_each example above, I'd still much prefer this:
#include <iostream>
#include <vector>
#include <algorithm>
void Output (int p_Value)
{
std::cout << p_Value << std::endl;
}
int main ()
{
std::vector<int> v = { 1, 2 };
for_each (v.begin(), v.end(), Output);
}
I can see the "for_each" I can see it calls Output with each iteration, that code clearly has a semi-colon, and I can go find "Output" very easily. I find this code is much cleaner and simpler to follow.
Labels:
C++,
c++11,
Code,
code patterns,
Coding Standards,
cpp,
Development,
examples,
lambda,
lambdas,
Naming Conventions; Coding Standards,
programming,
std,
stl
Tuesday, 18 March 2014
C++ (Passing by Reference) Programmer Insecurity
Sometimes even the smartest, and I mean proper mind blowing good, programmers can have an off day... And I just ran into one. Now, let me just say I consider myself a good C++ programmer, and an excellent Pascal programmer, I prefer C++ however and am taking every chance I get to use it, and use it cross platform.
Now, the chap I'm talking about, is a bit of a mega brain, he is really good, but he's just had a massive problem on his hands, and I don't think he liked my input...
First of all, he was working with "int", on a 32bit machine... But he was using this "int" as an unsigned value... So why not use "unsigned int"... apparently, he is having the kind of day where he doesn't want to type "unsigned " everywhere, so I suggested, replace "int" with "uint" and do "typedef unsigned int uint;" the silence was palpable...
He did it, and his code started to work, but it seemed he really wanted to just continue having an off day, rather then have me intereject with a solution...
Secondly, when he was done, he was getting a lot of memory used, he reckoned his code was fast and not creating copies... "int ValueReturn (int _lhs, int _rhs)".... A second suggestion, which I gave him via another member of staff "void ValueReturn (const uint& _lhs, const uint& _rhs, uint& _retVal)" to cut down the number of parameter copies and return copies he makes also went down like a tonne of bricks, he's stormed off, moaning that "that lot are a bunch of know it alls"
I can relate to his frustration, he's had a major problem, and we've effectively solved it for him in a few seconds, with what could be assumed to be obscure pieces of C++ code, but these practices of passing by reference, and even constant reference, and if you want to avoid creating more memory returning a reference to an [in/out] parameter is all normal fair. Scott Meyer points this out a lot in his books.
I think I grew out of this tantrum "my code is right, the system is wrong" attitude about three years ago, it feels longer, but I know it takes a long time for a programmer to be comfortable enough with their ability to sit back and say "yeah, I got that wrong" and fix it, rather than think admitting fault lowers their value to everyone. I certainly know from experience that those programmers who sit on their pedestal and are never challenged about shoddy code they do (even code which gets the job at hand done) will never progress and improve.
Anyway, something funny...
Anyway, something funny...
Labels:
C++,
Code,
code patterns,
Compiler,
cpp,
Development,
Language,
programmer,
programming
Subscribe to:
Posts (Atom)