Coding: Fleeting Thoughts

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 6578
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Fri Sep 27, 2013 4:47 am UTC

With regard to exceptions 90% of the time, I don't want to handle exceptions, I just want my program to fail. This makes C a PITA. Yes, it is nice to know exactly what exceptions can be thrown and in what cases, but even with C it needs to be documented. I'm sure there's a middle-ground somewhere, but hell if I know what it is.
Summum ius, summa iniuria.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11128
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Fri Sep 27, 2013 4:58 am UTC

I sort of want a language where a function can return multiple disjoint return types.

Callers of the functions must handle each and every possibility, even if it is just to discard and ignore it.

The downside? It requires non-linear programming to really work, because each function can both return what it says it returns, or some strange alternatives.

When you call "GetImageManager", it could return an image manager, or it could return an allocation error.

So now we need some way to handle the allocation error return type case without getting in the way of the core "usual" program flow.

... which ends up looking a lot like exceptions. Alternatively, you could imagine "collapsed code" attached to each function call that you can dive into.

The downside of the "collapsed code" case is that you cannot tell what a line of code does without looking at it. So it needs to be both visible, and not in the way. How to do that without exception-like syntax is ... puzzling.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Fri Sep 27, 2013 4:59 am UTC

Yakk wrote:Exceptions have serious problems in pretty much every language I've used them in.
...
I get the usefulness of dealing with exceptional error cases through some kind of exception handling mechanism, but they all seem to utterly suck.
To be honest... I agree.

But I despise error return codes. It makes it so you can't even do simple things like foo(bar()) if bar can possibly return an error.

It also interacts really poorly with the external world. Lots of C++ behaviors are communicated via exceptions. I don't know whether Google actually compiles with -fno-exceptions or just prohibits internal use, but if it's the former (and people do do the former) then you break a lot of standards behavior. And it means external consumers of your library have to deal with your choice too. I really really like the Google Test library for C++ unit tests, but the fact that their ASSERT_xx macros communicate by having a builtin return (to return from the test case) instead of throwing an exception has been really annoying on a number of occasions.

Do they really tell you to printf?
Yup.

"Do not use streams, except where required by a logging interface. Use printf-like routines instead.

There are various pros and cons to using streams, but in this case, as in many other cases, consistency trumps the debate. Do not use streams in your code."


And again, it's like exceptions, just less extreme. Streams aren't perfect, but printf has more moderately-big problems.

Not sure why std::function is more annoying than lambda: std::function without lambda is often a mess.
It still allows you to abstract over actual functions and function objects without using templates. I've used boost::function many times in my code, which can't use C++11.

I rarely see code that requires much more than 80 characters per line that wouldn't be improved with some in-statement line breaks and tabbing.
"I" inspired a whole religious wars thread on that, which I've been meaning to respond too but I've been too busy this week. :-)

Do you prefer always putting it on its own line? Seems ... bulky.
I don't know. I can't decide. Right now I do (there are exceptions), but it is a bit bulky. I think maybe an ideal rule would be if the return value is a short, single-token word it doesn't have to.

But that's neither here nor there -- the Google guidelines say that you're not to add a break, with no provision for the more complicated scenarios I give, unless it actually doesn't fit on the same line. And they break the first parameter onto the next line before splitting the return type off, e.g. their example

Code: Select all

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
    Type par_name1,  // 4 space indent
    Type par_name2,
    Type par_name3) {
  DoSomething();  // 2 space indent
  ...
}

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Fri Sep 27, 2013 6:30 am UTC

Yakk wrote:[spoiler]
EvanED wrote:Things I have more than a moderate dislike of:
  • The "absolute" prohibition on using namespace, even in source files

In local scope, sure. In an entire source file?

I think a lot of people have a needless aversion to importing an entire namespace. Why place artificial limits on how you can manage your namespaces? I don't use C++ much, so some of this might be misguided, but I know in Python people always object to "from Module import *" when there are actually a few good uses for it.

You might have a namespace full of utility functions for a problem domain that serve as your base functionality for a program. Why shouldn't they go in the global namespace where they belong? If you want everything in a namespace in the global one, what would you do? Bring them all in individually? Prefix them all with a namespace qualifier? Copypaste the whole thing?? Presumably the second is what whole-namespace-import-haters would choose, but that option makes things a lot less readable. First there's the sheer bloat of making all the names longer, plus adding a namespace qualifier makes the identifier stand out as "something from outside", when it's actually part of your basic building blocks.

Also, what if you wanted to "extend" a namespace to create a new one, or combine two namespaces?

Yakk wrote:I sort of want a language where a function can return multiple disjoint return types.
Callers of the functions must handle each and every possibility, even if it is just to discard and ignore it.
The downside? It requires non-linear programming to really work, because each function can both return what it says it returns, or some strange alternatives.
When you call "GetImageManager", it could return an image manager, or it could return an allocation error.
So now we need some way to handle the allocation error return type case without getting in the way of the core "usual" program flow.
... which ends up looking a lot like exceptions.
So now we need some way to handle the allocation error return type case without getting in the way of the core "usual" program flow.
... which ends up looking a lot like exceptions. Alternatively, you could imagine "collapsed code" attached to each function call that you can dive into.
The downside of the "collapsed code" case is that you cannot tell what a line of code does without looking at it. So it needs to be both visible, and not in the way. How to do that without exception-like syntax is ... puzzling.

Mayber rather than returning a disjunction of types, you could return a conjunction - ie. multiple return values. (Ideally, the additional values would be optional (with a default null value if necessary)). Then you could return either a normal value, or a null value together with some sort of error value. Implemented well, like in Common Lisp, the extra return values are invisible unless you ask for them. I think you have to ask for them in the function call itself, but you could add a facility that lets you query it later (although you'd need some syntax that lets you identify the function call.)

If you did use disjoint return types, I guess you'd need pattern matching to handle every function call. You could abstract some of the overhead away with something like monadic "do notation". You'd have something like Haskell's Maybe or Either types.

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Fri Sep 27, 2013 7:20 am UTC

troyp wrote:If you did use disjoint return types, I guess you'd need pattern matching to handle every function call. You could abstract some of the overhead away with something like monadic "do notation". You'd have something like Haskell's Maybe or Either types.

I was going to say this as well. You can mimic pattern matching on Either in C++ by returning a boost::variant and using a local visitor, although it's rather verbose. If I'm right there's also an implementation of "optional" going around somewhere which mimics Maybe.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

User avatar
phlip
Restorer of Worlds
Posts: 7572
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Coding: Fleeting Thoughts

Postby phlip » Fri Sep 27, 2013 7:32 am UTC

Yakk wrote:Either the language requires that every exception that a function can throw be described in language or not.

If it does require it, this leads to huge exception interface bloat. You either end up erasing nearly every exception into some new type, which then gets erased again, or you almost completely lack any typing to exceptions (they become glorified error strings), or other mess results.

If you don't require it, it is even worse. If you fail to reliably document what kinds of exceptions your function returns, users of functions are completely in the dark asto what the functions real interface contract is. If you succeed, you have to basically do the previous step manually: check every function you call, check its exception documentation, either document you pass the exception through or catch it locally, and you get next to no support from your compiler to keep this documentation in sync.

But what's your alternative? If you try to be more explicit, and make error conditions actual return values from your function, you're still going to have the same dichotomy. Either all your possible error returns are part of the declared type (and "PossibleError<FILE *, file_not_found> open(filename);" isn't much of an improvement over "FILE * open(filename) throws(file_not_found);", bloat-wise)... or they aren't, and you have to look at the docs for every function not just to see what errors it's capable of returning, but how it's returning them (maybe it returns the error codes directly, like COM's HRESULT values, or maybe it returns a sentinel and stores the error details elsewhere, like POSIX's errno and Win32's GetLastError(), or maybe it's doing something thoroughly crazy for various reasons)... and trying to embed an error sentinel in-band within your actual return value has a lot of problems in and of itself (Windows doesn't accept negative timestamps (ie before 1/1/1970) just so that its date manip functions can return -1 on error and have it be an out-of-bound value).

Really, getting rid of exceptions in and of itself doesn't save you from the problem on either side of the equation here.

If you were to try to build up a reasonable structured error-handling system sans-exceptions by using decorated return values and making the whole thing some language privilege and syntactic sugar to make "foo(bar(x))" easily codeable when bar() could return an error condition without having to directly put a branch between the two calls... I think, at the bare minimum, you'd end up with something like BASIC's horrible "On Error Goto" feature. Add some structure, so you have actual blocks of code rather than just gotos, and you essentially have try/catch. Add "finally" for syntactic-sugar's sake.

At this point, the main thing separating you from full-blown Exceptions we all know and love is the ability to not catch an exception and let it propagate up the stack. And, on this point, I'm on your side... they should probably have to be explicit about this, it shouldn't be the default. There could be some syntactic sugar to avoid you having to write "catch (SomeException) { throw; }" but there should still be something explicit there to say "I know this is an exception that can happen here, and am actively saying it's not my problem". The ability to wrap the exception in a different exception type for encapsulation (when a library calls another library and needs to return its own exception types instead of that other library's) without erasing the error information from that other exception (ie exception chaining) is also important.

Another point is it would be nice to have some way to handle "debugging" exceptions... ie exceptions that should never happen in a properly-written program, regardless of user input, but can happen if the code is buggy. Things that you'd be happy using "assert" for. I'm talking things like calling typesafe-printf with the wrong number/type of parameters. Every function that takes a pointer/reference that can't be NULL. Things that can appear all over the place, but which you don't want to have to handle individually in production code, and want to be as noisy as possible in debug code. In theory, this is what RuntimeException is for in Java (though in practise that class is abused for so much more than this), and in general is the main argument towards not declaring all possible exceptions. In the return value world, you can just ignore the response, throw in an assert if you're feeling fancy... but in the "force the coder to handle all exceptions" world, there needs to be something here.

On the other hand, having some mechanism for "ignorable exceptions" tends to be where the problems start coming in... every lazy Java programmer ever has at some point made an exception a subclass of RuntimeException just because they didn't want to have to deal with it now, and then become locked in and unable to change it... at which point the whole checked-exception idea can just be scrapped for the whole project, because there's no guarantee that a function that doesn't declare exceptions can't throw something we care about. And it can mean a way for the language to throw a lot of potential exceptions, all over the place... like in Python, it's impossible to make a nothrow function, as pretty much any single line of code could potentially throw KeyboardInterrupt at the very least. And these are real arguments against exceptions... in that they don't apply to other error-handling methods (you'd never see a POSIX function return an error code and set errorno to E_USER_HIT_CTRL_C). But the arguments in that quote aren't "these are why exceptions are bad", they're just "these are why error-handling is hard".

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
PM 2Ring
Posts: 3713
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Coding: Fleeting Thoughts

Postby PM 2Ring » Fri Sep 27, 2013 9:13 am UTC

Yakk wrote:It reads like "please write C++ like java" a bunch.

I don't know much about C++ or Java, so maybe I'm way off the mark, but maybe the intention is to make it easier to port code written in C++ to Java. Which makes sense given Google's connection to Android, and Java being the primary development language for Android.

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: Coding: Fleeting Thoughts

Postby Diadem » Fri Sep 27, 2013 9:49 am UTC

The problem with exceptions are that they are so often abused. I've seen code like

Code: Select all

try {
  a/b;
}
catch (...) {
  cout << "division by zero!";
}


Which is just an insane way of using exceptions, made worse by the fact that it doesn't even work (c++ does not throw an exception upon division by zero). If you know a specific condition that can go wrong, just testing for it before hand is so much easier. You still encounter code like this all the time though.

And even when properly used exceptions are often poorly documented, and not very clear. When I see a try block around a function call, I have no idea if the function can even return an exception - and no easy way of finding out. Good documentation helps, but good documentation is often a problem. I honestly don't know how to solve this though. There problem, as someone above pointed out, is not so much the exception syntax, but rather the fact that error handling is hard.

One thing I hate though is how bloated try/catch blocks make code.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Fri Sep 27, 2013 10:24 am UTC

One way to improve exception syntax a little and get them "out of the way" would be to add a special keyword that can handle exceptions in functions and methods (maybe even in blocks), so an implicit try would wrap the whole thing. Maybe:

Code: Select all

int foo(int x) {
    do_code();
} catching (ExcType err) {
    do_exception_handling();
}

- and if you wanted better syntax for multiple exception types, you could add case syntax to the catching block:

Code: Select all

int foo(int x) {
    do_code();
} catching (ExcType err) {
    case ExcSubtype1:
        do_exception_handling1();
    case ExcSubtype2:
        do_exception_handling2();
    default:
        do_default_exc_handling();
}


re: hidden interface: You could just require exceptions to be part of the function signature (whatever type of syntax you use). You'd need a bunch of typedefs for common cases.

re: erasing information: You could give exceptions a "previous exception" attribute which you can point at the caught exception if you're throwing a new one. Then anyone up the chain has access to the entire linked list of exceptions.

You could also leave the choice of error value/exception up to the caller: You'd have a sum type (eg. "Int|OverflowError") as return value as Yakk mentioned above: when an error occurs, you return an error object. The caller can be typed "Int|OverflowError" and use whatever pattern matching syntax you choose to deal with the error... OR the caller only accepts an Int. In this case, instead of the compiler giving a type error as usual, it inserts pattern matching code around the function call, and if an error is thrown at runtime, that code raises an exception (you could even use the exact same objects for the "raised" exceptions and the "passed" error values)
...not sure how this would work out - it was just a thought.

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 6578
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Fri Sep 27, 2013 10:13 pm UTC

Fuck Microsoft. I just spent 5 minutes looking for "diff" in TFS, only to have to go to stack overflow and figure out that they called it "compare". Seriously, fuck you, Microsoft. Life is short, I don't need to waste my time because you hire UI designers to design tools for programmers as if they are like your typical windows end-users.
Summum ius, summa iniuria.

User avatar
PM 2Ring
Posts: 3713
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Coding: Fleeting Thoughts

Postby PM 2Ring » Sat Sep 28, 2013 3:52 am UTC

Microsoft has an ancient tradition of doing things different to Unix (and other pre-established standards), going back to when Bill Gates thought it'd be a good idea to use backslash as the directory separator. What makes it more than merely annoying is that so many people feel that the Microsoft Way is the standard way and that everybody else are the non-conformists.

OTOH, I do have to admit that Microsoft isn't as bad in this respect as it used to be, eg modern versions of Internet Explorer are a lot more conformant to W3C standards than the old versions were.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Sat Sep 28, 2013 11:29 am UTC

I don't know how anyone can possibly use Windows without Cygwin and/or MinGW (or one of the other unix environments, at least). Well, maybe you could if you knew the MS scripting APIs and all the third party windows applications back to front, but who can be bothered learning stuff that only works on Windows?

User avatar
headprogrammingczar
Posts: 3072
Joined: Mon Oct 22, 2007 5:28 pm UTC
Location: Beaming you up

Re: Coding: Fleeting Thoughts

Postby headprogrammingczar » Sat Sep 28, 2013 11:55 am UTC

PM 2Ring wrote:Microsoft has an ancient tradition of doing things different to Unix (and other pre-established standards), going back to when Bill Gates thought it'd be a good idea to use backslash as the directory separator. What makes it more than merely annoying is that so many people feel that the Microsoft Way is the standard way and that everybody else are the non-conformists.

OTOH, I do have to admit that Microsoft isn't as bad in this respect as it used to be, eg modern versions of Internet Explorer are a lot more conformant to W3C standards than the old versions were.


See also: git. Seriously, SVN is the only one to get the verbs right (and CVS too, which SVN is based on). Commit, diff, update, add, rm, cp, mv, every command is named after exactly what it is!

The backslash has a fairly interesting history as a path separator, tracing all the way back to IBM. http://blogs.msdn.com/b/larryosterman/archive/2005/06/24/432386.aspx
<quintopia> You're not crazy. you're the goddamn headprogrammingspock!
<Weeks> You're the goddamn headprogrammingspock!
<Cheese> I love you

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Coding: Fleeting Thoughts

Postby troyp » Sat Sep 28, 2013 12:50 pm UTC

headprogrammingczar wrote:The backslash has a fairly interesting history as a path separator, tracing all the way back to IBM. http://blogs.msdn.com/b/larryosterman/archive/2005/06/24/432386.aspx

*snort* He's blaming IBM because they used / for command line switches, but then he admits he doesn't even know if it came from IBM or Microsoft. ;-)

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Sat Sep 28, 2013 2:38 pm UTC

I found this in some old code I wrote ages ago.

Code: Select all

   @SuppressWarnings("unchecked")
   protected static Class<? extends State<ActorStateData>>
   state(@SuppressWarnings("rawtypes") Class<? extends State> arg) {
      return (Class<? extends State<ActorStateData>>) arg;
   }


It seems to be a type casting function, because actually casting to "Class<? extends State<ActorStateData>>" everywhere was annoying or something. No matter what the justification, that is one seriously eye-grating function.
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Sat Sep 28, 2013 3:50 pm UTC

troyp wrote:I don't know how anyone can possibly use Windows without Cygwin and/or MinGW (or one of the other unix environments, at least). Well, maybe you could if you knew the MS scripting APIs and all the third party windows applications back to front, but who can be bothered learning stuff that only works on Windows?
Personally, I tend to stay away from Cygwin/MinGW as much as I can. I use utilities that come with it a fair bit, but use the shells minimally. I've always considered them to cause more problems that they solve (e.g. you can't always use paths from one in the other).

headprogrammingczar wrote:See also: git. Seriously, SVN is the only one to get the verbs right (and CVS too, which SVN is based on). Commit, diff, update, add, rm, cp, mv, every command is named after exactly what it is!
I would say to a large extent that's because you probably learned SVN first. With a Git mentality, there are a number of things about Subversion that don't make sense. For instance, take git checkout. If you're in a Git mindset, this is just unifying things that Subversion splits across three commands (update -r, switch, and revert) even though they are all doing the same thing. (In Subversion's case you could argue that revert should be separated out because it doesn't need to contact the repository.) Even for the case that is potentially really most confusing to Subversion-to-Git transplanted users, the very different use of the revert command, IMO doesn't innately make more sense in one or the other. Both are entirely reasonable uses.

Not to say that Git doesn't have naming problems. My favorite is the fact that you use git "add" to "update" a file in the "staging area", which is also known as the "index", and then to view differences between the committed copy and the index you use git diff --"cached". (The latter is also possible via git diff --staged, but that was added later and removing it from the list still leaves four terms for the same damn thing.

troyp wrote:*snort* He's blaming IBM because they used / for command line switches, but then he admits he doesn't even know if it came from IBM or Microsoft. ;-)
My guess is that his offhand comment about DEC is probably most accurate. Wikipedia says "development of 86-DOS took only six weeks, as it was basically a clone of Digital Research's CP/M (for 8080/Z80 processors), ported to run on 8086 processors", and while CP/M doesn't appear to have built-in commands that use / for switches (it didn't use any special lexical mark for switches from what I can tell), if they pulled over a bunch of people from DEC to work on MS-DOS then they would have been familiar with DEC's VMS, which did use / for switches.

Even if that's not true, it's not like MS was making the decision in a vacuum. I don't know my computer history that well, but I suspect that between things like VMS and IBM mainframe OSs, it wasn't particularly clear that Unix was the system to follow conventionwise even if MS had wanted to align with the future market leader.

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: Coding: Fleeting Thoughts

Postby Ubik » Sat Sep 28, 2013 7:53 pm UTC

What GCC needs is a Game of Life backend.

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: Coding: Fleeting Thoughts

Postby Diadem » Mon Sep 30, 2013 8:19 am UTC

So today I found c++ code looking like this:

Code: Select all

double function(param1, param2, param3)
double param1, param2;
int param3;
{
    <function body>
}


It seems to compile, so I guess it's allowed, but what the heck? I've never seen this before and Google gave me nothing on syntax like this. I couldn't even find anything about this in the c++ standard. Note that there's no semicolon at the end of the first line, so it's not just a function declaration, nor is there a colon, so the 2nd and 3rd line are not a function initializer.

What's going on here? Is it just specifying the type of the parameters in a deliberately obtuse way, or is more happening here?

Whatever it's going on, it's terribly unreadable. Especially because in the orginal code there was about 2 pages of commented out code between the first and second line. Visual Studio doesn't like it either, it marks the opening brace as an error ("Expected a declaration"), though like I said before, it does compile.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

User avatar
PM 2Ring
Posts: 3713
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Coding: Fleeting Thoughts

Postby PM 2Ring » Mon Sep 30, 2013 8:53 am UTC

Weird. That's old K&R 1 function declaration style. I don't know much about C++, but I'd be surprised if it were supported by any sane C++ compiler. And even in straight C mode it ought to generate a warning (at the very least).

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Mon Sep 30, 2013 9:52 am UTC

Yes, that's K&R style and it means exactly the following.

Code: Select all

double function(double param1, double param2, int param3)
{
    <function body>
}


As far as I know that should be beyond deprecated in C++, but the Microsoft compiler is probably forgiving about it.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

User avatar
Xenomortis
Not actually a special flower.
Posts: 1448
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Mon Sep 30, 2013 9:58 am UTC

Jplus wrote:As far as I know that should be beyond deprecated in C++, but the Microsoft compiler is probably forgiving about it.

Not to my knowledge; I couldn't coerce it to compile anything in that style.
Image

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: Coding: Fleeting Thoughts

Postby Diadem » Mon Sep 30, 2013 11:27 am UTC

I took a closer look at the project file, and this is what it said:

Code: Select all

    <ClCompile Include="file_in_question.c">
      <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsC</CompileAs>
      <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">CompileAsC</CompileAs>
    </ClCompile>


So it looks like it's explicitly being compiled as c, presumably because it's not legal c++.

It almost looks like whoever wrote this was being deliberately obscure. Like I said, in the original file there is 2 pages of commented out code between the first line of the function and the K&P style variable definitions, and also some new lines between the parameter definitions and the start of the function body. Furthermore the entire file is duplicated elsewhere, but this time in proper C++ syntax, except for horribly breaking encapsulation. Both versions are used of course, in different parts of the program. And when I say 'duplicated' I meant 'almost duplicated', because there's just enough differences in white space to make an easy comparison impossible, and there seems to be one functional difference in a border-case that is never used anyway (except that it is, in one place, but that code is in turn preceded by an #ifdef statement that seems to be always false).

Ah well. I should probably think of badly written, poorly maintained, code as job security :)
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Mon Sep 30, 2013 12:50 pm UTC

Jplus wrote:Yes, that's K&R style and it means exactly the following.

Code: Select all

double function(double param1, double param2, int param3)
{
    <function body>
}
It actually doesn't mean exactly that, because the compiler doesn't perform type checking (or even # of parameters checking, apparently) on calls to functions defined using K&R style:

Code: Select all

#include <stdio.h>

void foo1(a, b)
  int a;
  double b;
{
    printf("%d, %lf\n", a, b);
}

int main()
{
    foo1(1, 1000000, 1000000);  // not so much as a warning with GCC 4.4.7, even with -Wall -Wextra
    foo1(); // same
    return 0;
}

User avatar
Xenomortis
Not actually a special flower.
Posts: 1448
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Mon Sep 30, 2013 12:51 pm UTC

Really?
Way to trash your stack...

Edit: Actually, your stack will probably survive, if you get to the cleanup stage.

Edit 2:
Actually, this dates from before I was born. What were the common CPU conventions of the time? Stack passing is typical on x86, but not on other architectures.
Image

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: Coding: Fleeting Thoughts

Postby Diadem » Mon Sep 30, 2013 1:54 pm UTC

I've been reading up on K&R style. Apparently in old k&R C you could leave the return type and parameter type out entirely, and it would be assumed to be int. That was still legal in C90 but not C99 it seems. K&R style also gives a function declaration and definition, but not a prototype. Prototypes didn't yet exist in K&R C. That's the reason why you can call them with a different number of parameters.

This is a coding style that was deprecated something 3 decades ago. I think I can forgive myself for initially being confused about it :)

It does further strength my hate-love relationship with C++ though.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

User avatar
phlip
Restorer of Worlds
Posts: 7572
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Coding: Fleeting Thoughts

Postby phlip » Mon Sep 30, 2013 2:13 pm UTC

Right... my usual example:

Code: Select all

charat(s, x)
char *s;
{
  return s[x];
}

Code: Select all

// separate file without even any prototypes at all
// could also declare "charat()" as a forward declaration, or "int charat()", or "charat(char*,int)" or some other combination of things.
// Also note: not including stdio

main()
{
  printf("Third char of Hello is: %c\n", charat("Hello", 2));
  return 0;
}
It compiles with 6 warnings with gcc -Wall -Wextra, but it compiles, and is valid C, for certain definitions of "valid C". The warnings, for the curious: implicit return type of charat, implicit parameter type for x, implicit return type of main, implicit declaration of charat, implicit declaration of printf, implicit declaration of printf differs from the standard definition (because it's implying it takes those two parameters instead of varargs).

If you see this in code that's less than about 25 years old, then some face-punching is probably warranted.

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Mon Sep 30, 2013 3:43 pm UTC

Xenomortis wrote:Really?
Way to trash your stack...

Edit: Actually, your stack will probably survive, if you get to the cleanup stage.

Edit 2:
Actually, this dates from before I was born. What were the common CPU conventions of the time? Stack passing is typical on x86, but not on other architectures.


???
Isn't the return pointer at the beginning of the stack frame, so that you can't overwrite it with arguments or local variables or what have you. Or maybe it's after the parameters? Either way, I know C sometimes doesn't seem to care too much about how many arguments you supply to a function.

I mean, obviously in C++ this is going to lead to memory leaks and what have you, but in C it's usually fine.

You can even do stuff like this

Code: Select all

#include <stdio.h>

void myputs(c)
char* c;
{
        int i;
        for (i = 0; i < 4; i++) {
                puts(*(&c + i));
        }
}
int main() {
        myputs("Some men", "just want to ", "watch the world", "burn");
}


Compiles (with gcc on x86) to print

Code: Select all

Some men
just want to
watch the world
burn


Here's the assembly
Spoiler:

Code: Select all

        .file   "test.c"
        .text
.globl myputs
        .type   myputs, @function
myputs:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $0, -12(%ebp)
        jmp     .L2
.L3:
        movl    -12(%ebp), %eax
        sall    $2, %eax
        leal    8(%ebp), %edx
        leal    (%edx,%eax), %eax
        movl    (%eax), %eax
        subl    $12, %esp
        pushl   %eax
        call    puts
        addl    $16, %esp
        incl    -12(%ebp)
.L2:
        cmpl    $3, -12(%ebp)
        jle     .L3
        leave
        ret
        .size   myputs, .-myputs
        .section        .rodata
.LC0:
        .string "burn"
.LC1:
        .string "watch the world"
.LC2:
        .string "just want to "
.LC3:
        .string "Some men"
        .text
.globl main
        .type   main, @function
main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        subl    $4, %esp
        pushl   $.LC0
        pushl   $.LC1
        pushl   $.LC2
        pushl   $.LC3
        call    myputs
        addl    $16, %esp
        movl    -4(%ebp), %ecx
        leave
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.4"
        .section        .note.GNU-stack,"",@progbits
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Mon Sep 30, 2013 3:55 pm UTC

Usual stack frame layout looks like that:
argument n
argument n-1
...
argument 2
argument 1
return pointer
previous frame pointer if compiling with frame pointers
<---- new frame pointer points here (i.e. ebp on x86)
local 1
local 2
...
local m-1
local m
<--- stack pointer points here (i.e. esp on x86)

So it indeed doesn't matter if you pass additional arguments to a function.
Last edited by korona on Mon Sep 30, 2013 3:58 pm UTC, edited 1 time in total.

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Mon Sep 30, 2013 3:58 pm UTC

Ah, so the return pointer is on top. Then it shouldn't really matter how many arguments you supply. The calling function will know how many to clean up anyway.
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

User avatar
Xenomortis
Not actually a special flower.
Posts: 1448
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Mon Sep 30, 2013 3:58 pm UTC

You, sir, name? wrote:Isn't the return pointer at the beginning of the stack frame, so that you can't overwrite it with arguments or local variables or what have you.

I was more thinking about not providing enough arguments. If the function expects three but the caller only pushes two onto the stack (the x86 assumption), the function will start grabbing random values.
The "stack trashing" would come from callee cleanup (Win32) - the called function would remove too much, corrupting the caller's stack. C traditionally uses caller cleanup, so it should survive.
Alternatively, it alters a parameter that wasn't actually passed, also corrupting the caller's stack.
I think the return address might get overwritten if really extreme mis-matches happen (function expects a struct to be passed that it intends to modify, you pass it something smaller).

can you even do that in K&R? *
*dagger because struct* means something very different.

But I'm speculating; all my knowledge relates to conventions and contexts that postdate K&R C.
Image

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Mon Sep 30, 2013 4:09 pm UTC

You can trash the return pointer by something like this (assuming you are not compiling with -fomit-frame-pointer or any optimizations that reorder locals):

Code: Select all

void f() {
    int local;
    int *p = &local;
    p++; // now points to the previous frame pointer
    p++; // now points to the return pointer
    printf("return pointer: %p\n", *p);
    *p = 0;
    // segfault on return
}


You cannot trash it by passing not enough arguments:

Code: Select all

void f(int a, int b, int c) {
    // a points to the last local of the previous stack frame
    // b points to the second last local of the previous stack frame
    // c points to the third last local of the previous stack frame
}
...
f();

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Mon Sep 30, 2013 5:05 pm UTC

You, sir, name? wrote:Ah, so the return pointer is on top. Then it shouldn't really matter how many arguments you supply. The calling function will know how many to clean up anyway.
This has more-or-less been said, but this here is the key point: your calling convention can be either caller- or callee-cleanup, and what that determines is who pops the arguments from the stack. It's totally possible to do either, with the exception of vararg functions which must be caller-cleanup. (And functions defined K&R style have to be caller-cleanup or you greatly increase your chances of stack corruption when you do something wrong.) Because of vararg and K&R functions, caller-cleanup is popular for C programs but it's not the only choice; for example thiscall, stdcall, and fastcall calling conventions for x86 are callee-cleanup.

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Mon Sep 30, 2013 5:38 pm UTC

Calling conventions that pass arguments in registers are of course superior to the standard gcc x86 calling convention.
I guess gcc still uses it because of backwards compatibility.

I presume caller-cleanup conventions are a bit faster than callee-cleanup conventions. x86 does have a "return and then pop n bytes from the stack" instruction but I guess it is actually slower than a usual "return" instruction followed by an "add" to adjust the stack pointer on modern hardware.

User avatar
Xenomortis
Not actually a special flower.
Posts: 1448
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Mon Sep 30, 2013 6:10 pm UTC

x86 calling conventions pass parameters on the stack because x86 hasn't got many registers.
x86-64 got a load more registers, so the calling conventions used by Microsoft and *nix systems pass the first few parameters using registers and have caller cleanup.

I don't know of any speed difference between caller and callee cleanup, but callee cleanup will use fewer bytes for functions used more than once.
Image

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Mon Sep 30, 2013 6:35 pm UTC

Well it depends on the function that get called. If the function uses their arguments immediately passing them in registers is faster. If they would get spilled anyways passing them on the stack might be faster or equally fast but never slower. I think passing two arguments in edi and esi might be a good tradeoff. But the advantage won't be great enough to switch to another convention.

The difference in binary size is negligible for desktop hardware (caller-cleanup requires 2 bytes per call for less than 256 bytes of arguments, otherwise 5 bytes; callee-cleanup requires 2 bytes per function)

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Coding: Fleeting Thoughts

Postby EvanED » Mon Sep 30, 2013 7:04 pm UTC

korona wrote:Calling conventions that pass arguments in registers are of course superior to the standard gcc x86 calling convention.
I guess gcc still uses it because of backwards compatibility.
On AMD64, the primary calling convention puts the first six word-sized arguments into registers, and GCC follows suit. MS puts fewer in, but there are still four. The fastcall calling convention on x86 as well puts two or three arguments (depending on compiler) into registers, and thiscall puts one (this :-)).

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: Coding: Fleeting Thoughts

Postby TheChewanater » Mon Sep 30, 2013 7:13 pm UTC

Well, this is fun.

Code: Select all

void magic(x) int* x; {
    (*x)++;
}

void foo(int* x) {
    magic();
    
    printf
("x = %i\n", *x);
}

int main() {
    int x = 10;
    foo(&x);
}
 
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
Xenomortis
Not actually a special flower.
Posts: 1448
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Mon Sep 30, 2013 7:20 pm UTC

korona wrote:The difference in binary size is negligible for desktop hardware (caller-cleanup requires 2 bytes per call for less than 256 bytes of arguments, otherwise 5 bytes; callee-cleanup requires 2 bytes per function)

Now, certainly.
Maybe not when these conventions were set.

EvanED wrote:MS puts fewer in, but there are still four

The MS convention is weird. IIRC, space is reserved on the stack as spill space for those registers (rcx/dx r8/9), although you're not required to use it for such.

TheChewanater wrote:Well, this is fun.
Spoiler:

Code: Select all

void magic(x) int* x; {
    (*x)++;
}

void foo(int* x) {
    magic();
   
    printf("x = %i\n", *x);
}

int main() {
    int x = 10;
    foo(&x);
}


I'm sure there's a special place in hell for people who do that. :wink:
Image

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Mon Sep 30, 2013 7:24 pm UTC

Xenomortis wrote:
EvanED wrote:MS puts fewer in, but there are still four

The MS convention is weird. IIRC, space is reserved on the stack as spill space for those registers (rcx/dx r8/9), although you're not required to use it for such.

Yeah.

gcc has a "red zone", a 128 byte area beyond the end of the stack that functions are allowed to use without changing the stack pointer. When a signal occurs the kernel routines have to increment the stack pointer by at least 128 bytes before invoking the handler to make sure that this area is not corrupted. A nice source of confusion if you try to run some kernel module and you forget to append -fno-red-zone to flags. The system crashes randomly when an interrupt handler corrupts the red zone.

User avatar
skeptical scientist
closed-minded spiritualist
Posts: 6142
Joined: Tue Nov 28, 2006 6:09 am UTC
Location: San Francisco

Re: Coding: Fleeting Thoughts

Postby skeptical scientist » Mon Sep 30, 2013 10:57 pm UTC

skeptical scientist wrote:Things I hate about XCode:
  • It randomly hangs while I'm not doing anything but typing text and spins a pinwheel for a while (when it doesn't just crash).
  • It stores a ton of configuration data under the hood where it is inaccessible except through a complicated GUI so you have to search through the GUI to figure out why things are breaking even though the code you wrote is obviously correct.
  • It maintains its own directory structure for projects which can have nothing to do with either the actual filesystem directory structure or which files belong to which targets. Why???
  • It lets me accidentally edit read-only files, so it's constantly fighting with perforce.
  • If I close windows in the wrong order, next time I start xcode I have to tell it which interface panels I want to see all over again.

  • Everything about how xcode builds a project is determined by environmental variables which change unpredictably at runtime. I tell xcode I don't only want to compile for the active architecture only, and I see it dutifully "setenv ONLY_ACTIVE_ARCH NO". Then later in my build a step fails because "ONLY_ACTIVE_ARCH=YES".
  • Apple makes changes in each version of xcode which causes an xcode build which previously did the right thing to start doing the wrong thing, without letting you know that anything changed.
  • The number of times I have used source control to get a diff of changes to my foo.xcodeproj/project.pbxproj file and then used a text-editor to strip out bad changes that are impossible to revert inside xcode is ridiculous. Doing it at all would be ridiculous, but I find that I do this almost every time I have to change something about how my product builds.
I'm looking forward to the day when the SNES emulator on my computer works by emulating the elementary particles in an actual, physical box with Nintendo stamped on the side.

"With math, all things are possible." —Rebecca Watson


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 7 guests