Showing posts with label friend. Show all posts
Showing posts with label friend. Show all posts

Monday, 23 October 2023

Job Interview : switch(char) again! Twenty years on!

I've spoken about some of the strange interviews I've enjoyed over the years; notable engineering highlights were having test driven development spooned to me, when I simply didn't know the particular syntax they were demanding, or having StringBuilder suggested for a C# task where they actually needed to cross-core thread their problem, or pointing out that the CI solution being described would lead to an alien environment of folks being paralysed by fear of red flags; only to witness their development floor being paralysed by exactly those issues as I walked out the door.

Today however, I'm going to go way back and talk about one of the very early development interviews I had; this maybe like the third interview I ever had after graduating, it was with a rail rolling stock company, in a very bright and sunny room with three guys who seemed to know their entire universe was correct and they'd take no new information into their sphere of understanding.

I share this one as a friend of mine was in touch over the weekend, he had an interview, and strangely he had the exact same conversation I had... THE EXACT CONVERSATION, twenty something years later.

Let us start by sharing some perfectly valid C++ code:

bool validate(const char& value)
{
switch (value)
{
case 0: return true;
case 'x': return true;
default: return false;
}
return false;
}
int main()
{
const bool isValidOne{ validate('a') };
const bool isValidTwo{ validate('x') };
const bool isValidMe{ validate(12) };
}

Read this code, we should see an output (if we output the bools) of "false", "true" and "false"... I think we can all agree.  This is perfectly valid C++.

It isn't very clean nor pretty, we're relying on the compiler standard decaying char to int and vice versa; which is not very nice and I wish compilers would complain here, that we're comparing int to char in the first case and we're converting the integer 12 to char, I think it should really strong type this.

But that is not the point, the point is that both in my friends case and mine all those years ago the interviewers got really uppity about that "switch" statement.  They insisted that you can not "switch" on a character type...

Just flat insisted.

I remember in my experience they had me write up what they wanted on a blackboard, with chalk, and they wanted me to go through the code line by line; I wrote more or less the above, they insisted they had a character not a byte (uint8_t: though there was no standard for this at the time) and then they went off on this tangent amongst themselves about the value of the if-elseif-else stanza's they insisted would work over my solution above.

Now I was talking to these guys in the early 00's, my friend however was talking to them in 2023.  He jumped on this with the point of "well if the character is known at compile time, yes then constexpr maybe of use" and he showed them:

if constexpr(foo) and if consteval(bar) examples.

He described their faces screwing up and two of the chaps he was talking to staring at each other before making subtle notes on their paper.

It was very clear at this point that the CTO performing the interview was not keeping his knowledge up to date, the group seemed to cluster onto the idea of just maintaining what they have, stagnation like this in our joint experience can be problematic.  Both team morale and high turn over of engineers always happens when things stagnate.

I have to say in my interview I actually sat it out from beginning to end and just knew I was not interested in the role; and I did not hear from them again.

My friend however, he was genuinely concerned he'd done a good interview and they'd be offering him a role, they kept him talking for over an hour and a half!

He's a late 40's engineer of 25+ years experience, just like me; the three chaps interviewing him, including the CTO, were ten or more years his junior, it seemed they were looking to back up their team with an older head to lend legitimacy to things; and they had lost two original engineers of their project to literal retirement without replacing them.

He saw the issues and he thanked them for his time and left, with a negative to whether he would be interested in going further.  This stunned them.

It has to be said, so many interviewers forget that though they are interviewing you, you are also interviewing them like most all human interaction it is a two way street and it's crucially important to remember that in a job interview.  We spent a significant amount of our time at work, if you are not comfortable there or feel you are going to be acting out a role instead of filling one and making it your own.

Tuesday, 13 September 2016

Software Engineering : C++14 Factory Create Pattern

For the GCC version of this, see this post (at the time of writing this link is to a scheduled item, it may not work just yet, scheduled for Tuesday 4th October 2016 at 18:30 GMT+1).

I commonly employ a "Factory Create" style to my classes in C++, the reason being I like to take control over the specific way that classes can be created, rather than relying on the compiler to always create the default constructors for me.  I like the constructors, but it leads to the problem of memory leaks, lets take a look at a bad example:


With this class any programmer can then do:

Alpha* l_somevalue = new Alpha(42);

They can then forget to call "delete l_somevalue;" and therefore leak memory.

We would therefore like to hide the constructors away, to prevent their miss use:


This necessitates we do expose someway to create an instance of the class, and that instance be wrapped as a smart pointer...


The first thing we see is a forward declaration of the class, then it's type wrapped as a smart pointer (a shared pointer in this case).  Next we see the various constructors have been made public, curing and removing all possibility of the programmer leaving hanging pointers to instances of our class.

Finally, we see a set of static "create" functions, our method to create an instance of the class now would look like this:

BetaPtr l_instance = Beta::Create();

When the reference count to the l_instance copy drops to zero then it will be automatically cleaned up, this is the whole point of this programming pattern.

There is nothing wrong with this code at the moment, we can define the constructors, knowing they can't be used outside the class, like this:


And the create functions look like this:


So far, we've learned nothing new... So what is new?... Well in C++14, really we should not be calling "new" at all.  The moment we see new outside the class we should implement this whole code pattern to hide the constructors away, exposing only smart pointers.  And likewise internally to those create functions we should not be calling "new", we should be using "std::make_shared".

Like this:


The problem?...


Hmmm... "error C2248: 'Beta::Beta': cannot access private memory declared in class 'Beta'", what is this telling us?

That the constructor of Beta could not access one of the private members of itself?  Huh?.. That makes no sense, of course the constructor should be able to access its own member!

What else does the error tell us?  "File: memory"... memory?... Not my code file, but the memory header?  We can open that location and take a look, what is the location of the error representing?


The _Ref_count_obj, the actual location that a shared_ptr is instantiated and the forward passed the arguments to the constructor, so the error is NOT that the constructor of our class can't get at it's private member, but that the template wrapped reference count object, which we inherit from because of the use of "make_shared", that reference can't get at the private members of the class!

How to solve this?... Simple:


Simply adding the "friend std::_Ref_count_obj<Beta>" allows that inherited class from "<memory>" to see inside the private area of the Beta class, and we enable the clean use of std::make_shared thereafter.

I highly recommend removing your constructors from prominence, and the use of "Factory Creation", as to other much more authoritative figures then myself.

My Complete code is below!

#include <iostream>
#include <memory>


class Alpha
{
private:

int m_AlphaValue;

public:

Alpha();
Alpha(const int& p_NewValue);
Alpha(const Alpha& p_OtherAlpha);

const int Value();
void Value(const int& p_NewValue);
};



class Beta;
using BetaPtr = std::shared_ptr<Beta>;
class Beta
{
private:

friend std::_Ref_count_obj<Beta>;

int m_AlphaValue;

Beta();
Beta(const int& p_NewValue);
Beta(const Beta& p_OtherBeta);

public:

static BetaPtr Create();
static BetaPtr Create(const int& p_NewValue);
static BetaPtr Create(const BetaPtr& p_OtherBeta);

const int Value();
void Value(const int& p_NewValue);
};


Beta::Beta()
:
m_AlphaValue(42)
{
}

Beta::Beta(const int& p_NewValue)
:
m_AlphaValue(p_NewValue)
{
}

Beta::Beta(const Beta& p_OtherBeta)
:
m_AlphaValue(p_OtherBeta.m_AlphaValue)
{
}

BetaPtr Beta::Create()
{
return std::make_shared<Beta>();
}

BetaPtr Beta::Create(const int& p_NewValue)
{
return std::make_shared<Beta>(p_NewValue);
}

BetaPtr Beta::Create(const BetaPtr& p_OtherBeta)
{
return std::make_shared<Beta>(*p_OtherBeta.get());
}




int main()
{
Alpha* l_Instance = new Alpha(42);
std::cout << "Alpha: " << l_Instance->Value() << std::endl;

BetaPtr l_BetterInstance = Beta::Create(42);
std::cout << "Beta: " << l_BetterInstance->Value() << std::endl;

// No need to delete l_BetterInstance, it cleans itself up

// Whoops... l_Instance of alpha leaks!
}