Fantastic exchange from DConf

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Tue May 9 22:26:11 PDT 2017


On Wed, May 10, 2017 at 01:32:33AM +0000, Jack Stouffer via Digitalmars-d wrote:
> On Wednesday, 10 May 2017 at 00:30:42 UTC, H. S. Teoh wrote:
> > 		strncpy(tmp, desc->data1, bufsz);
> > 		if (fwrite(tmp, strlen(tmp), 1, fp) != 1)
> > 		{
> > 			fclose(fp);
> > 			unlink("blah");
> > 			return IO_ERROR;
> > 		}
> > 
> > 		strncpy(tmp, desc->data2, bufsz);
> > 		if (fwrite(tmp, strlen(tmp), 1, fp) != 1)
> > 		{
> > 			fclose(fp);
> > 			unlink("blah");
> > 			return IO_ERROR;
> > 		}
> 
> I think you cause a memory leak in these branches because you forget
> to free tmp before returning.

Well, there ya go. Case in point. Even when you're consciously trying to
write "proper" C code, there are just far too many ways things can go
wrong that slip-ups are practically inevitable.

Eventually, the idiom that I (and others) eventually converged on looks
something like this:

	int myfunc(blah_t *blah, bleh_t *bleh, bluh_t *bluh) {
		void *resource1, *resource2, *resource3;
		int ret = RET_ERROR;

		/* Vet arguments */
		if (!blah || !bleh || !bluh)
			return ret;

		/* Acquire resources */
		resource1 = acquire_resource(blah->blah);
		if (!resource1) goto EXIT;

		resource2 = acquire_resource(bleh->bleh);
		if (!resource1) goto EXIT;

		resource3 = acquire_resource(bluh->bluh);
		if (!resource1) goto EXIT;

		/* Do actual work */
		if (do_step1(blah, resource1) == RET_ERROR)
			goto EXIT;

		if (do_step2(blah, resource1) == RET_ERROR)
			goto EXIT;

		if (do_step3(blah, resource1) == RET_ERROR)
			goto EXIT;

		ret = RET_OK;
	EXIT:
		/* Cleanup everything */
		if (resource3) release(resource3);
		if (resource2) release(resource2);
		if (resource1) release(resource1);

		return ret;
	}

In other words, we just converged to what essentially amounts to a
try-catch block with the manual equivalent of RAII. After a while, this
is pretty much the only way to have any confidence at all that there
isn't any hidden resource leaks or other such errors in the code.

Of course, this is only the first part of the equation. There's also
managing buffers and arrays safely, which still needs to be addressed.
We haven't quite gotten there yet, but at least some of the code now has
started moving away from C standard library string functions completely,
and towards a common string buffer type where you work with a struct
wrapper with functions for appending data, extracting the result, etc..
IOW, we're slowly reinventing a properly-encapsulated string type that's
missing from the language.

So eventually, after so much effort chasing down pointer bugs, buffer
overflows, resource leaks, and the rest of C's endless slew of pitfalls,
we're gradually reinventing RAII, try-catch blocks, and string types all
over again. It's like historians are repeating each other^W^W^W^W^W I
mean, history is repeating itself. :-D  It makes me wonder how much
longer it will take for us to gradually converge onto features that
today we're enjoying in D. Will it take another decade of segfaults,
untraceable pointer bugs, security holes, and memory leaks?  Who knows.
I'm secretly hoping that between now and then, D finally takes off and
we can finally shed this dinosaur age language that should have died
after the 70's or latest the 80's, yet still persists to this day.


> Side note: scope(exit) is one of the best inventions in PLs ever.

Ironically, D has gone so far past the woes that still plague C coders
every day that scope(exit) is hardly ever used in D anymore. :-P  It has
its uses, certainly, but in my regular D code, I'm already benefitting
so much from D's other features that I can hardly think of a use case
for scope(exit) anymore, in the context of idiomatic D.  I do regularly
find myself wishing for scope(exit) in my C code, though!


T

-- 
Век живи - век учись. А дураком помрёшь.


More information about the Digitalmars-d mailing list