Forum search & shortcuts

Writing good softwa...
 

[Closed] Writing good software

Posts: 7127
Full Member
Topic starter
 

What have you got against std::shared_ptr<> oldnpastit?

Work pretty well in my experience. Certainly a lot less error prone than managing raw pointers . The only real issue I've had is with programmers who are still trying to write in C and just don't understand them.

They are slow - there's an extra load of locking/unlocking that goes on in the background. Usually that's OK, but sometimes it can cause weird slowdowns.

Worse than that is it encourages program design that doesn't think up-front about how lifetimes and ownership will work.

It also goes horribly wrong if people take the raw pointer out of a std::shared_ptr<> and then stuff it into another, unrelated shared or unique ptr. While you might think that people shouldn't do this (and you would be right) it turns out that they do.

Also they look really ugly.


 
Posted : 19/09/2017 1:29 pm
Posts: 31206
Full Member
 

They are slow

Slower, yeah, but [i]slow[/i]? As in measurably so? Not IME. Not unless you are absolute hammering them with billions of objects. (And if you are sharing billions of objects then your code is.. interesting).

Bear in mind that if you [i]don't[/i] use them then you may need to roll your own thread-safe code to manage the lifetime of raw pointers to dynamically allocated resources. That's not easy! It often ends up with [i]more[/i] overhead than shared_ptr. Plus it's a major source of bugs.

Worse than that is it encourages program design that doesn't think up-front about how lifetimes and ownership will work.

Lifetimes and ownership don't matter though - that's the point. A shared object has shared ownership and lives as long as at least one owner still cares. That's perfect.

In contrast smart pointers [i]force[/i] people to make up-front design decisions about whether an object should be unique, shared or weak - something you can't even express with raw pointers.

e.g. if a public method returns a raw pointer then I need to go trawl the source to figure out if I need to delete it or not (and also [i]how[/i] to delete it). If that method changes those ownership semantics later, then it can impact my code without me even noticing.

But if the method returns a smart pointer then I know exactly where I am and any later changes to that design are explicit in the signature.

It also goes horribly wrong if people take the raw pointer out of a std::shared_ptr<> and then stuff it into another, unrelated shared or unique ptr. While you might think that people shouldn't do this (and you would be right) it turns out that they do.

Yep true. Those people are C programmers pretending to write C++. 🙂 That should be caught at peer review.

But likewise there are a hundred and one ways to get raw pointers horribly wrong and many of them are very hard to catch.

In [url=

]"Effective Modern C++"[/url] Meyers say:

...we might try to enumerate the reasons why a raw pointer is hard to love:

1. Its declaration doesn’t indicate whether it points to a single object or to an array.

2. Its declaration reveals nothing about whether you should destroy what it points to when you’re done using it, i.e., if the pointer owns the thing it points to.

3. If you determine that you should destroy what the pointer points to, there’s no way to tell how. Should you use delete, or is there a different destruction mechanism (e.g., a dedicated destruction function the pointer should be passed to)?

4. If you manage to find out that delete is the way to go, Reason 1 means it may not be possible to know whether to use the single-object form (“delete”) or the array form (“delete []”). If you use the wrong form, results are undefined.

5. Assuming you ascertain that the pointer owns what it points to and you discover how to destroy it, it’s difficult to ensure that you perform the destruction exactly once along every path in your code (including those due to exceptions). Missing a path leads to resource leaks, and doing the destruction more than once leads to undefined behavior.

6. There’s typically no way to tell if the pointer dangles, i.e., points to memory that no longer holds the object the pointer is supposed to point to. Dangling pointers arise when objects are destroyed while pointers still point to them.

Raw pointers are powerful tools, to be sure, but decades of experience have demonstrated that with only the slightest lapse in concentration or discipline, these tools can turn on their ostensible masters.

(Very good book by the way)

Also they look really ugly.

I'll give you that one too. But C++ rarely an elegant looking language. 😆


 
Posted : 19/09/2017 2:55 pm
Posts: 2626
Full Member
 

I am primarily a C programmer. While C's raw pointers can be the source of a lot of trouble it is at least nice to think that I don't have to understand quite as many different ways to handle pointers (and therefore how to handle each sort of pointer safley) as I would in C++.


 
Posted : 19/09/2017 3:01 pm
Posts: 31206
Full Member
 

C raw pointers are a [i]bit[/i] easier compared to C++ raw pointers, but there is a good reason why various safety standards (e.g. MISRA C) completely ban the use of dynamically allocated memory.

Experience shows it is just too dangerous.


 
Posted : 19/09/2017 3:13 pm
Posts: 2626
Full Member
 

My limited exposure to MISRA suggests that it's a right old barrel of laughs.

One friend had some sort of connection to a group that contributed to MISRA or something similar. His impression was that the people assigned to work on it were put there to keep them from doing the harm they'd inflict if assigned to real software projects.


 
Posted : 19/09/2017 3:23 pm
Posts: 31206
Full Member
 

It's a pretty tough standard - but it's also the reason your car doesn't randomly stop because it has run out of memory 😀


 
Posted : 19/09/2017 3:48 pm
Posts: 7097
Free Member
 

It's easy to work around MISRA - write it in assembler instead.

If you can't do it in assembler, you probably have an over-complex design.


 
Posted : 19/09/2017 3:51 pm
Posts: 31206
Full Member
 

Or, y'know, just stick to statically allocated memory.


 
Posted : 19/09/2017 3:55 pm
Posts: 6259
Full Member
 

If you can't do it in assembler, you probably have an over-complex design.

Surely if you can't do it in assembler, then neither can the compiler? Unless commercial compilers have secret access to the hidden / undocumented processor features, and you're doing something weird and very low level (that probably needs C with inline assembler anyway) ?


 
Posted : 19/09/2017 4:04 pm
Posts: 0
Free Member
 

One of the things I like about some of the unit test frameworks is that I can structure my tests into a series of assertions, or even use case descriptions, of how my software components work.

That, in conjunction with good naming and good software partitioning into focused libraries/dlls, can provide good 'living' documentation for the system that someone can be sure is still relevant, unlike technical documentation produced at the start of a project.

We're using Mocha at the moment for unit up to end-to-end tests and the descriptions are structured to provide even nicer documentation.


 
Posted : 19/09/2017 4:10 pm
Posts: 31206
Full Member
 

Even writing embedded stuff we rarely go down to assembler, except for the really really low-level hardware interface stuff where you need to get clock cycle timing perfect.


 
Posted : 19/09/2017 4:12 pm
Posts: 0
Free Member
 

[img] ?oh=c5f826b7a51867baeb73bf31bb50da15&oe=5A413E77[/img]


 
Posted : 19/09/2017 4:13 pm
Posts: 9158
Full Member
 

1. You can't write your own crypto. You might think you can but you can't, so save your users a lot of trouble and use the platform crypto instead. And make it easy to upgrade.

2. Parsing XML is difficult, so use the platform XML parser. It will save you so much trouble in the future.

3. When you are ripping off Open Source components, credit the teams that wrote it and make their code easy to upgrade.

4. Leave your ego behind. It might be code you have spent hours, days or weeks on, but that should not stop you from accepting that it may have bugs. The last thing a project needs is someone getting defensive and not fixing problems.

5. Architect and design the system defensively. There will be security vulnerabilities in your code, so if the whole system is geared up to defend agains them, it will be less painful to fix.


 
Posted : 19/09/2017 4:18 pm
Posts: 31206
Full Member
 

[code]"1" + 1 == 0
'1' + 1 == '2'
'1' + '1' == 'b'[/code]

Damn C 😀


 
Posted : 19/09/2017 4:19 pm
Posts: 11402
Free Member
 

I like c++ but working as a 3ds max developer have to put up with shit like this on a daily basis 😀

BOOL ShapePickMode::HitTest(IObjParam *ip, HWND hWnd, ViewExp *vpt, IPoint2 m,int flags) {
if (!mpEditPoly) return false;
if (!ip) return false;
return ip->PickNode(hWnd,m,this) ? TRUE : FALSE;
}

this is relatively mild rubbish from the sdk I just happen to have open when reading this thread


 
Posted : 19/09/2017 4:22 pm
Posts: 31206
Full Member
 

Klunk: Mmmmm....

Poorly named parameters? Check.
Unused parameters? Check.
Hiding errors as valid return values? Check.
Passing pointers when they should probably be references? Check.
Not using const? Check.
Not using the built in true/false constants? Check.

All good stuff. To be fair though, you can write a crap API in just about any language.


 
Posted : 19/09/2017 4:33 pm
Posts: 0
Free Member
 

The golden rule of writing good software is "look at how Garmin have done it then do the exact opposite".


 
Posted : 19/09/2017 4:37 pm
Posts: 7097
Free Member
 

Or, y'know, just stick to statically allocated memory.

I do, I do. My current gig is genuinely easier in assembler.


 
Posted : 19/09/2017 4:58 pm
Posts: 0
Free Member
 

Anybody else waiting eagerly to see what molgrips has to say ? 🙂


 
Posted : 19/09/2017 5:01 pm
Posts: 31206
Full Member
 

My current gig is genuinely easier in assembler.

Blimey, that's rare these days.

We tend to design in a C/C++ interface layer on top of any assembler stuff. That way we can build the suitable Mocks/Fakes/Stubs/Spies for unit testing purposes against that interface without necessarily needing them to run on the target hardware.


 
Posted : 19/09/2017 5:08 pm
Posts: 106
Free Member
 

Still do most of my work in assembly as well (low power DSP/comms stuff). Except for the actual assembler for the DSP we use, which I wrote myself in nice clean portable C 🙂

And since unbelieveably no-one's posted it yet, here's the [url=

XKCD[/url] ...

[img]


 
Posted : 19/09/2017 5:11 pm
 kcal
Posts: 5450
Full Member
 

Definitely the engineering rule of "pick two" applies for software -
- cheap (quickly written)
- light/fast (performance)
- strong (robust/reliable)

I had the misfortune to try and maintain a colleague's piece of code, I think it was a TIFF manipulation routine. C. I swear to god there was *a* routine, and best part of 3,000 lines of code. I may have wept.

10. When you find yourself writing stuff "just in case" or for future upgrades, be very wary!


 
Posted : 19/09/2017 5:14 pm
Posts: 91169
Free Member
 

Anybody else waiting eagerly to see what molgrips has to say ?

I'm not reading this thread.


 
Posted : 19/09/2017 5:16 pm
Posts: 31206
Full Member
 

[code]Exception in molgrips.cpp: molgrips not found.[/code]


 
Posted : 19/09/2017 5:30 pm
Posts: 3323
Full Member
 

2. Two mutexes good, four mutexes bad.

2. Two mutexes good, four mutexes bad. [b]No mutexes best[/b].

here are some more .......

Adding extra unwanted stuff because they think it might be needed later

Not refactoring

Editor inheritance

Not handling the unexpected

Saying they are complete when what they actually mean is 'I've nearly done typing it in and I think it will compile'


 
Posted : 19/09/2017 5:34 pm
Posts: 54
Free Member
 

Surely just create a mock/fake/stub molgrips and have it say whatever you like?


 
Posted : 19/09/2017 7:06 pm
Posts: 31206
Full Member
 

Because molgrips is written in Java.

So first we'll need an AbstractMolgrips class produced by an AbstractMolgripsFactory, realised by a RealMolgripsFactory and MockMolgripsFactory then we'll want an AbstractMolgripsFactoryFramework to take care of selecting the factory... ...and probably some Beans too for good measure... 😉


 
Posted : 19/09/2017 8:09 pm
Posts: 1653
Full Member
 

SELECT name
FROM stwers
WHERE bighitter = TRUE
AND currently_flouncing = FALSE
AND unwilling_mac_user = TRUE
;

Or something. I don't do this for an actual living, which is probably for the best where our codebase is concerned.


 
Posted : 19/09/2017 8:32 pm
Posts: 7097
Free Member
 

We tend to design in a C/C++ interface layer on top of any assembler stuff.

I've worked on a project with a C/C++ interface layer underneath the assembler stuff.

That was slightly leftfield.


 
Posted : 20/09/2017 8:54 am
Posts: 31206
Full Member
 

Yeah, that's an erm... [i]unusual[/i] design decision 😕


 
Posted : 20/09/2017 9:59 am
Posts: 78575
Full Member
 

'1' + '1' == 'b'

What the shit is this?


 
Posted : 20/09/2017 11:27 am
Posts: 31206
Full Member
 

What the shit is this?

In C the char type is an integer type (usually a 8 bits) and it just holds the [url=

value[/url] of the character.

So
[code]'1' + '1' == 'b'[/code]
works because it is equivalent to
[code]49 + 49 == 98[/code]

😀


 
Posted : 20/09/2017 11:39 am
Posts: 31206
Full Member
 

Can you figure out the other two though?

[code]"1" + 1 == 0
'1' + 1 == '2'[/code]


 
Posted : 20/09/2017 11:41 am
Posts: 78575
Full Member
 

Ok, so '1' + 1 == '2' is essentially the same - you're incrementing the ASCII value?

"1" + 1 == 0 - is that just a type mismatch, adding an integer to a string? Sounds like a tenuous explanation but...?

I should add, I've never touched C++ in my life. Beyond Pascal at Uni I've only really dabbled. Despite that I was actually employed as a programmer at one point, but that was glorified web dev building an intranet system. HTML pages with client-side Javascript and server-side VBscript, often with all three on the same line of code. Fun times.


 
Posted : 20/09/2017 11:47 am
Posts: 2559
Free Member
 

"Well, actually"

I see what the point is: "1" is a pointer to an array of characters, specifically two characters '1' (ascii 49) and '<turns out its impossible to write backslash-zero here>' (ascii 0, the null byte). Adding to an array is moving the pointer, so the pointer now points at the null byte.

But if you put that into a compiler then that isn't what it would say...

const char *s = "1";
puts(s); // prints '1'
puts(s+1) // prints a blank line


 
Posted : 20/09/2017 11:57 am
Posts: 31206
Full Member
 

Right on the first one. '2' follows '1' in the ASCII table so the addition sort of "works".

The second one is a bit more subtle. Strings in C don't really exist. They are really just a pointer to a block of characters that terminates with a null character.

So adding 1 to it just shifts the pointer along by one element, so that it is now pointing at the null character, which is zero.

(in actual code you'd need to dereference the pointer with a * to read its contents - but that's just confusing things further)


 
Posted : 20/09/2017 12:00 pm
Posts: 78575
Full Member
 

So in terms a brain whose strongest programming language is Pascal can understand,

A string in C has a nul terminator at the end (I think I knew this somewhere at the back of my brain next to the cheat codes for Jet Set Willy).

Adding an integer to a string is handled as an array so you step into it.
So "dave/0" + 1 == "ave/0" (using the wrong slash here so it displays)?


 
Posted : 20/09/2017 12:06 pm
Posts: 31206
Full Member
Posts: 31206
Full Member
 

Yep, that's it Cougar.

Though it's not really that it "is handled as an array", it's really that you are doing pointer arithmetic by adding a value to the pointer.

The pointer is just the memory address where your "dave/0" characters are stored, let's say 0x00114000.

When you add an integer to a pointer, C (and C++) interprets that as "add this number, multiplied by the size of the things I'm pointing to, to this pointer address"

So you "dave/1" + 1, becomes 0x00114000 + (1 * sizeof(char)).
So, assuming that on your system a char is one byte, the pointer ends up being 0x00114001. If a char is two bytes it would be 0x00114002. Either way it would end up pointing to the next char.

Array notations in C are really just a prettier way of doing exactly the same pointer arithmetic.

This might help:


 
Posted : 20/09/2017 12:18 pm
Posts: 78575
Full Member
 

Fascinating. yes, thanks.

What's the asterisk signify in the variable declaration?


 
Posted : 20/09/2017 12:37 pm
Posts: 2626
Full Member
 

It indicates that s is a pointer to a character. Or to an array of characters, i.e. a string, which is actually the case. See an earlier post in this thread about how C pointers don't distinguish between the two.


 
Posted : 20/09/2017 12:48 pm
Posts: 78575
Full Member
 

Aha. Ta.


 
Posted : 20/09/2017 12:49 pm
Posts: 31206
Full Member
 

The asterisk is part of the type.

[code]char *s[/code]

says that [i]s[/i] is a pointer to a char.

Whereas

[code]char s[/code]

would say that [i]s[/i] is a char.

Edit: too slow. 🙂


 
Posted : 20/09/2017 12:50 pm
Posts: 7097
Free Member
 

Yeah, that's an erm... unusual design decision

More of a certification decision. C'est la vie.


 
Posted : 20/09/2017 1:13 pm
Posts: 216
Full Member
 

I can't believe there is a thread about good software and no-one has mentioned Uncle Bob.

Follow the teachings of Clean Code. This is a must read for all my Devs.


 
Posted : 20/09/2017 2:12 pm
Page 2 / 3