• (cs)

    Why hasn't anyone made an American Airlines joke yet?

  • DB (unregistered) in reply to TGV
    TGV:
    ochrist:
    Wouldn't the Hungarians find that offensive and racist?
    Jobbik considers the Hungarians a superior race, and consequently will declare war upon the poor OP. Then the EU will step in and say: hey, you can't declare war like that! We will lower your agricultural subsidies. Then the Hungarians will say that they have been victimized by a racist, and the EU will turn against the OP and extradite him to the US, where the president's daughter has not been seen since she went to a health clinic in ... Hungary!

    Sounds like the president's father will have to utilize his special skills...

    [image]
  • Simon (unregistered) in reply to Nagesh
    Nagesh:
    Some comments are also colourful with email messages in them.

    /* Mail from John **Jun,30, 2008 We want to bypass this condition where it checks for specific branches. Don't care about report loading time. Make it happen.

    */

    That's not a WTF, that's very good practice, exactly the kind of thing comments should be documenting - that while the code might look bad, it's done that way for a reason.

  • (cs) in reply to ronpaii
    ronpaii:
    Cad Delworth:
    "All modern integrated development environments display variable types on demand"

    Obviously all you so-called "experts" never write any Office VBA code in Office 2010 or later?

    Right click on a variable and select "Quick Info".
    Heh: touché! But I've never ever needed to use that functionality in the 20-plus years I've been writing VBA, because I use Hungarian Notation for all my variable names. That means I never need to think "Now, what type is that variable again?" Hungarian Notation is a very useful aide memoire, provided you are rigourous about it.

    Office VBA is redundant now we have PowerShell, you say? Naah: I write new automation code in Office VBA for my employer all the time. Currently, what I'm mostly writing is code to automate the running of Crystal Reports, and the formatting of the results in Excel. This saves the company several man-weeks per year compared to when "someone" had to do those same tasks manually. It may not be bleeding-edge stuff, but it pays my rent.

    Would I want to try doing all that automation using PowerShell instead? NO! PowerShell is fine for the kind of sysadmin scripts that used to be done with VBScript and WMI: like listing the RAM available in every computer on the LAN, or managing AD, and maybe even writing some output into an Excel workbook. But for day-to-day automation tasks within Office, I'd rather use Office VBA every time: after all, it was written for the purpose!

    Plus, I need never worry about whether a given user's computer even has PowerShell installed: many sysadmins ensure that "user" computers DON'T have PowerShell on them.

  • (cs) in reply to Ragnax
    Ragnax:
    VBA was already on life support before Office 2010 ever launched. The emergence of Powershell pretty much sealed the deal, with system adminstrators now having a better scripting alternative than the old VBA scripting from the Windows Scripting Host.
    Patently you haven't grasped that VBScript and VBA (= Visual Basic for Applications) are two completely different languages.

    VBA is hosted within Office applications (with its own IDE, yet!); VBScript is, as you say, a language supported within WSH, and it has no IDE.

    FWIW, I agree that VBScript is dead/dying. But: I haven't yet seen any viable alternative to VBA. Suggestions on that are welcome, if anyone has any? And no, AutoIT and similar "action-scripting" applications don't count!

  • gnasher729 (unregistered) in reply to GeneralKnowledge
    GeneralKnowledge:
    ... are still integers since they reference a memory location. They do so by specifying the start of the referenced data structure as offset from the start of a process' address space.

    So, yes, char* is an integer. It refers to some (hopefully null-terminated) characters, but in itself it is an integer.

    Absolute bollocks. You have to unlearn a lot of nonsense that you learned, or you'll always be a danger to yourself and the rest of the world. You are confusing one method of implementing pointers that is currently in fashion with what pointers actually are. The fashion has changed, and can change again.

    Even in a language like C, believing that "pointers are integers" means you can't understand null pointers, function pointers, restrict and const restrict pointers, related pointers, thread specific storage, and lots of other things.

  • gc, (unregistered) in reply to Remy Porter
    Remy Porter:
    Nope, JavaScript is even worse when you use Hungarian notation, because you have no way to guarantee that the type in the variable is the type the Hungarian notation claims it is, and it's trivially easy to make a mistake which causes JavaScript to change the type of a piece of data into something you don't think it should be.

    Just because somebody hands you a variable called "strFoo" doesn't mean it contains a string. I once inherited code that had a variable named "intProjectNumber". The variable held things like "QR-1554-PL".

    If there are no compile-time type checks, all Hungarian notation does is add noise to your code and a false sense of security.

    Don't use Hungarian Notation, HOWEVER... I think it is sometimes useful to specify a type in a variable name. Not to assume what it is, but to identify what it's maent to be.

    If I was bughunting in an application where I found a variable intProjectNumber = "QR-1554-PL", I'd be having a good hard look at why it is so...(although I generally have the luxury of C so that sort of nonsense would make the world cry a lot). I don't really agree with hungarian notation per se', but I think it can be very useful to use snippets of it to show intent (and to some degree to identify variable scope gSomething immediately identifies that this feller might be global and if I play games with this little sucker I might break something else).

    When you work in an environment where you are (and always will be) the only person who fiddles with the code you can choose to {not} use whatever standards you want. When you are playing in a sand pit with lots of other script kiddies, it becomes important to have standards that can help all of you play together nicely. Hungarian Notation is probably at one extreme of the spectrum, however the idea "it is evil to embed type information in a avariable" is probably the polar opposite, and as with most things there's some middle ground somewhere that might be a better option...

  • (cs) in reply to gnasher729
    gnasher729:
    GeneralKnowledge:
    ... are still integers since they reference a memory location. They do so by specifying the start of the referenced data structure as offset from the start of a process' address space.

    So, yes, char* is an integer. It refers to some (hopefully null-terminated) characters, but in itself it is an integer.

    Absolute bollocks. You have to unlearn a lot of nonsense that you learned, or you'll always be a danger to yourself and the rest of the world. You are confusing one method of implementing pointers that is currently in fashion with what pointers actually are. The fashion has changed, and can change again.

    Even in a language like C, believing that "pointers are integers" means you can't understand null pointers, function pointers, restrict and const restrict pointers, related pointers, thread specific storage, and lots of other things.

    What about English Pointers?

  • PRMan (unregistered) in reply to Steve The Cynic
    Steve The Cynic:
    Remy Porter:
    Yes. Hungarian Notation is, and always has been, a WTF. If you're using a strongly typed language, it means that you don't trust the compiler or aren't prepared to deal with compiler errors. If you're using a weakly typed language, it demonstrates that you're trying to enforce strong-typing-by-convention- which doesn't work. It provides a false sense of type safety ("the variable is named strFoo, it must hold a string" is a statement of hope, not reality).

    In the name of being "self-documenting", it favors knowing what prefixes mean over knowing what the code does ("Oh, gStrFoo", it's a global, so I know to be careful about touching it. I don't know what or how this variable is used, but I know it's global! Self documenting!")

    What makes Hungarian Notation TRWTF, however, is popular it was. This I will never understand. I remember when I was a little n00b programmer, and on my first job, I was taught Hungarian Notation. Even then, I demanded to know, "Why?" The answer always was, "It's the standard. Do it."

    Tsk. You need to distinguish between Systems HN and Apps HN. The SHN habit of encoding the actual type of the variable in a prefix(1) to its name is indeed a WTF, albeit is relatively mild one.

    (1) Some folks like to use the prefix as the whole name. They deserve to be stabbed repeatedly.

    AHN, on the other hand, encodes useful information about what a variable is contaminated with rather than what its programming language type is. So for example, imagine a system where string variables might contain some sort of raw text, or they might contain some sort of XML-safe or HTML-encoded text, or they might contain SQL-cleansed text (whatever that means. We impose a reasonable rule that a variable contains a maximum of one sort of text, and mark it with the sort of ill the variable is contaminated with:

    std::string rawUserInput; // "raw" indicates raw text
    std::string xsafeProcessedInput; // "xsafe" indicates XML-safe text
    std::string sqlsafeDatabaseReadyInput; // "sqlsafe" indicates SQL-safe text
    We also provide conversion functions:
    xsafeProcessedInput = xsafeFromRaw( rawUserInput ); // OK
    xsafeProcessedInput = rawFromXsafe( rawUserInput ); // NOT OK
    The variable name warts align with the function name warts so that a code reviewer will be able to see that the first one is OK (passes raw-contaminated text to a "from raw" function that returns XML-safe text that is stored in an XML-safe variable) but the second is not (passes raw-contaminated text to a "from XML-safe" function that returns raw text that is stored in an XML-safe variable - a definite no-no).

    It's at best a very mild WTF, because it suggests a lack of confidence in the quality of the code. One specific advantage it gives (up to a point) is that you can use automated methods to validate the use of the correct type of contamination.

    Contextual notations such as "g" for globals, various prefixes or suffixes for member variables, and "p" for pointers are neither SHN nor AHN, and their use is debatable. I personally favour prefix "m_" as a notation for members in languages that don't require this. / self. / this-> to introduce their use. So Python doesn't get m_ prefixes because all member variables have to be accessed as self.thingy, but C++ does because you are allowed to just access m_thingy from a member function without saying this->m_thingy. I'm indifferent on the use of a "p" prefix for pointers, and generally opposed to global variables, whatever the naming convention.

    Sounds like you need a class with some properties that do conversions...

  • Matteo (unregistered) in reply to Whatever
    Whatever:
    Call me old-fashion, but I like to know the type of my variable without hovering over it with my mouse. People act like it's the devil, it's just a preference (mostly, depending how you use it).

    Userid = ...well it' a user id, I have no idea what goes in it, but it's a userid

    guidUserID = it's a userid, and it holds a guid....

    I still prefer the latter, you say you don't? Well that's fine too, but why keep hammering on it when it's just preference...

    Until one day, you userid changes representation, and is stored as a long int instead. Then you have to change both type and name of the variable everywhere you use it. And while you might remember, your colleague might not. It's brokenness waiting to happen.

    In most of your functions, the type should be easily inferred from either the formal parameter type, a local declaration. The exception are object members, of course.

    But if you need hungarian notation to clarify your code, there are good chances your code isn't clearly written to begin with. See "Clean Code" of R. C. Martin.

    Finally, instead of "guidUserID", a someone not in love with HN might want to write "userGuid"...

  • PAul M (unregistered) in reply to Whatever

    The difficulty is that the important thing about a variable is what it means, what its purpose is, what data it holds, not its type. The important thing about guidUserID is not that it is a GUID, but that it is a user id. The type is important, sure, as far as making the code mechanically work. But not to the logic and purpose of the code.

    Hungarian notation gets this backwards, putting the stuff that doesn't matter at the front of the name, and obscuring the business logic.

  • PAul M (unregistered) in reply to PRMan

    Well, that's not what most people mean by HN.

    In a similar vein what you are suggesting, primitive numbers that are not counts should always be suffixed with a unit of measurement.

    Never: int remainingTime; Always: int remainingTimeSec, remainingTimeMin, remainingTimeDays;

    This applies to variables, method names, parameter names, and member names. It saves a universe of hurt.

    Rule 2 is: never use floating point except for physical measurements accepted to be approximations or for statistical averages. In particular, never use it for units of (billable) time or for currency. Ever. Store these quantities as integer multiples of the smallest division you will need.

    Had one project which kept track of hours. Then a requirement came to track half hours, so they made time a float. Dear God, you would not believe the mess …

  • PAul M (unregistered) in reply to gnasher729

    Seconded. Simple, concrete example: on the x86 architecture, the first half of a 32-bit pointer was a page and the second half was offset within that page. Pages were interleaved 16 bytes apart, meaning that pointers were basically 5-byte quantities. Treating the whole 32 bits as an integer yields nonsense.

    An integer is a number. A number is something that you can add and multiply another number with. If it doesn't make sense to add and multiply it (e.g.: phone numbers, any sort of code), then it isn't an integer. That you can add 1 to a pointer to get another pointer isn't sufficient.

  • Norman Diamond (unregistered) in reply to Yazeran
    Yazeran:
    Well to be fair, at least the comment warned about the 'Complete'. It would have been more evil of the original coder to just leave the comment out and watch the fireworks when some future schmuck changed that text string....

    Now that we know that 'Complete' may have side effects, we know to be gun-shy about touching ANY text string in that monstrosity... :-)

    The original designer saw the title of Steve McConnell's book and wanted to make sure programmers would code the magic word.

  • Norman Diamond (unregistered) in reply to Remy Porter
    Remy Porter:
    I recognize that the real world makes it difficult to write short methods, keep variables scoped to very brief and easy to understand blocks. Hungarian notation is a great way to make 300 line functions readable. But maybe the problem is that you shouldn't be writing 300 line functions.
    Right, I should write three thousand 100 line functions instead of one thousand 300 line functions. As if that's really going to help debugging and maintenance. You were right when you recognized that the real world makes it difficult to simplify things that way.
  • Norman Diamond (unregistered) in reply to jay
    jay:
    I had a job once where IT management announced a new standard that in the future all program names must be sequence numbers. (With a prefix identifying what environment they ran in -- like Web vs GUI -- and what language they were written in.) So we had program names like NBC1057 and IOF3298. Of course the programmers objected that this made it very difficult to keep track of which program was which. Management's response was that a program name could never tell you exactly what the program did and might be misleading. So ... because a name can't be guaranteed to be complete and accurate, we should make it totally meaningless?
    You should have pointed out to them how IBM did it. IEHPROGM didn't have a serial number, it had a meaningful abbreviation telling system programmers that it was a program.
  • Iago (unregistered) in reply to Brad

    I think it may be VBA - which renders all the discussions about IDEs and CVS a bit off the mark.

  • (cs) in reply to SeySayux
    SeySayux:
    If you need to scroll more than 50 lines up or down to see the type of your variable, you're doing something wrong.
    Try writing a bytecode engine in that sort of size. Even with lots of common code factorized out, those things get large
  • (cs) in reply to faoileag
    faoileag:
    In C++, pointers are occasionally used. Pointers point to memory locations, in other words: are integers.
    Last time I checked, C++ neither demotes pointers to integers nor integers to pointers. Not even for the void* pointer type. You can of course use casts to shoot yourself in the foot as much as you want, but that really can't be stretched to mean "pointers are integers". Nope, sorry, in C++ pointers most definitely are not integers. The only things that "are" integers are types that are convertible to integers, and there are just a few of those in the core language. You can create new types convertible to integers, of course.

    If you have a pointer in C++ and you try to use it like you would an integer, you'll get a compiler error. Heck, with modern compilers even if you try to pass it off as integer as an argument to printf, you'll still get a warning.

    Besides, saying that pointers are "occasionally" used in C++ is a bit crazy IMHO. Yes, modern C++-11 APIs can be designed without the use of pointers without any loss of performance. Anything that predates C++-11 has no choice but to use pointers as necessary.

  • (cs) in reply to Matteo
    Matteo:
    Until one day, you userid changes representation, and is stored as a long int instead. Then you have to change both type and name of the variable everywhere you use it. And while you might remember, your colleague might not. It's brokenness waiting to happen.
    Never mind that in C++ for types that aren't numbers you're supposed to new types anyway. If it's not a number, don't use a number type for it. For GUIDs, most arithmetic operations don't make sense -- don't use an integer for crying out loud. The fact that you can store your particular GUID in an integer is an implementation detail and nobody's business, pretty much.

    Heck, C++ makes it quite easy to express the fact that something is a USER guid as opposed, to, say COMPANY guid, and those shouldn't be assignable to each other, for example. C++ makes expressing such basic business logic requirements reasonably easy in its type system, and such things are then checked at compile time.

  • (cs) in reply to Steve The Cynic
    Steve The Cynic:
    Tsk. You need to distinguish between Systems HN and Apps HN. The SHN habit of encoding the actual type of the variable in a prefix(1) to its name is indeed a WTF, albeit is relatively mild one.

    (1) Some folks like to use the prefix as the whole name. They deserve to be stabbed repeatedly.

    AHN, on the other hand, encodes useful information about what a variable is contaminated with rather than what its programming language type is. So for example, imagine a system where string variables might contain some sort of raw text, or they might contain some sort of XML-safe or HTML-encoded text, or they might contain SQL-cleansed text (whatever that means. We impose a reasonable rule that a variable contains a maximum of one sort of text, and mark it with the sort of ill the variable is contaminated with:

    std::string rawUserInput; // "raw" indicates raw text
    std::string xsafeProcessedInput; // "xsafe" indicates XML-safe text
    std::string sqlsafeDatabaseReadyInput; // "sqlsafe" indicates SQL-safe text
    We also provide conversion functions:
    xsafeProcessedInput = xsafeFromRaw( rawUserInput ); // OK
    xsafeProcessedInput = rawFromXsafe( rawUserInput ); // NOT OK
    I don't know why the heck you call this C++ code -- just because you use std::string doesn't mean it's C++. What you've shown is C code with char* replaced by std::string. Yeah, it needs a C++ compiler to compile, but stopping at replacing C string api with std::string might be a first step when you port a legacy project, not something that you boast about, OK? It's a WTF.

    When you use C++, you've got compiler to do all this checking and enforcement for you. Heck, you can even allow automatic conversions in certain circumstances. The fact that you resort to bogging down a human developer with tracking this stuff in variable names is beyond me. It's stupid, it's a clear indication that you don't know the most basic elements of design of code in C++.

    Never mind that std::string is a joke when you actually try to use it in a business application, it misses pretty much everything a real life application needs. QString and friends (or similar classes in other frameworks) is what you need to stay productive.

  • (cs) in reply to dkf
    dkf:
    SeySayux:
    If you need to scroll more than 50 lines up or down to see the type of your variable, you're doing something wrong.
    Try writing a bytecode engine in that sort of size. Even with lots of common code factorized out, those things get large
    Using modern template machinery of C++ you probably could do it in the sense that there would be one bytecode per line in the executor function.
  • (cs) in reply to PAul M
    PAul M:
    Rule 2 is: never use floating point except for physical measurements accepted to be approximations or for statistical averages. In particular, never use it for units of (billable) time or for currency. Ever. Store these quantities as integer multiples of the smallest division you will need.

    Had one project which kept track of hours. Then a requirement came to track half hours, so they made time a float. Dear God, you would not believe the mess …

    Given that a double is perfectly fit for use as an equivalent of a 53 bit 2s complement integer, I don't quite see what the problem is. Heck, if all you ever do is add half hours and no other fractions, it'll still work just fine and will provide you with exact results if the rounding mode is correctly set. Comparisons will work normally as well. You run into obvious problems when you try to use floating point for numbers that don't have a finite representation that fits into the bits you allocate for the fraction in the mantissa. Decimal fractions of any length are not representable in binary floating point and thus you definitely do not want to, say, store currency amounts as-is in floating point. You can store them once you multiply them by 100 so that they become representable in floating point. I had a lot of code that ran on 286 machines where I needed reasonably performing 32 bit integers. The double type was perfect for that purpose, and the math coprocessor did its job as it was designed to.
  • (cs) in reply to MiniMax
    MiniMax:
    Ahh - magic text strings!

    I have been hit by that too. I took over a system created by non-English speakers that for some reason had decided it was a good idea to log messages in English. [...] So now the code has been sprinkled with warnings like

    // OBS: Magic finish message. Used by MasterRouter to start processing the file.

    and // OBS: Magic size message. Used by ReportPreparation when when collecting event information.

    Oh, so it was a Swedish system, then :)

  • the Hungarian (unregistered) in reply to PAul M
    PAul M:
    The difficulty is that the important thing about a variable is what it *means*, what its purpose is, what data it holds, not its type. The important thing about guidUserID is not that it is a GUID, but that it is a user id. The type is important, sure, as far as making the code mechanically work. But not to the logic and purpose of the code.

    Hungarian notation gets this backwards, putting the stuff that doesn't matter at the front of the name, and obscuring the business logic.

    This is true only of the bastardized and corrupted "systems" Hungarian notation that arose from the Windows group's total failure to grasp Simonyi's original point.

  • (cs) in reply to Nagesh
    Nagesh:
    ObiWayneKenobi:
    And it's often done by clueless retards who don't understand the point of version control. My last job had a dude (old VB guy) who would litter the code with this crap; change logs, comments stating what bug was fixed, and the occasional "No clue why this is here but removing it breaks things :(" comment.

    When I asked him why he didn't put those in SVN, he gave some BS answer about it being easier to see the comment/note in code instead of having to hunt through the logs.

    Partly I agree with the old guy. Speed of checking comments in code is much higher than sifting through subversion comments and trying to find out what is happening.
    Well, if you insist on sifting through subversion comments, you have only yourself to blame. Speaking of blame, any semi-decent version control system nowadays provides blame functionality. Heck, any semi-decent GUI for your VCS will support nicely formatted blame as well. That's what you need, that's what it was designed for. No need to hunt anything. Just look at that line and see what was the message for the most recent change to it. You can also go to preceding revision(s) while you're at it to see how it changed over time. It's magic, I tell you.

  • (cs) in reply to Steve The Cynic
    Steve The Cynic:
    faoileag:
    In C++, pointers are occasionally used. Pointers point to memory locations, in other words: are integers.
    Go away and study the history of C and C++ before you spout nonsense like this. On most modern platforms, a plain pointer is a single integer-like value, but there have been exceptions.
    Dude, but this is an implementation detail that's hidden by C++. In C++, no pointer types are default convertible to integers, and no integers are default convertible to pointers except for the literal zero that's taken to mean NULL -- it's a special case. What the underlying platform is doing is completely irrelevant because when you actually write C++ code, pointers don't behave like integers. So please, stop spewing nonsense. The history is irrelevant, what the machine-level implementation does is irrelevant. What matters is that in C++ pointers don't behave like integers, are not integers, and don't mix with integers. If they happen to have the same size as one of the integer types, well, so what, that doesn't make them integers, you know. There's an almost infinite supply of types that can have same size as any other type, you know. The only limitation is due to identifier length restrictions.
  • (cs) in reply to gnasher729
    gnasher729:
    GeneralKnowledge:
    ... are still integers since they reference a memory location. They do so by specifying the start of the referenced data structure as offset from the start of a process' address space.

    So, yes, char* is an integer. It refers to some (hopefully null-terminated) characters, but in itself it is an integer.

    Absolute bollocks. You have to unlearn a lot of nonsense that you learned, or you'll always be a danger to yourself and the rest of the world. You are confusing one method of implementing pointers that is currently in fashion with what pointers actually are. The fashion has changed, and can change again.

    Even in a language like C, believing that "pointers are integers" means you can't understand null pointers, function pointers, restrict and const restrict pointers, related pointers, thread specific storage, and lots of other things.

    Seconded. The implementation details of pointers are fairly irrelevant -- you don't need to know them in order to use pointer types correctly, and depending on those details definitely leads to non-portable code. While C implicitly converts between integers and pointers, and also between incompatible pointer types to POD (sane compilers warn about all that, through), C++ most definitely doesn't do that.

  • (cs) in reply to chubertdev
    chubertdev:
    gnasher729:
    GeneralKnowledge:
    ... are still integers since they reference a memory location. They do so by specifying the start of the referenced data structure as offset from the start of a process' address space.

    So, yes, char* is an integer. It refers to some (hopefully null-terminated) characters, but in itself it is an integer.

    Absolute bollocks. You have to unlearn a lot of nonsense that you learned, or you'll always be a danger to yourself and the rest of the world. You are confusing one method of implementing pointers that is currently in fashion with what pointers actually are. The fashion has changed, and can change again.

    Even in a language like C, believing that "pointers are integers" means you can't understand null pointers, function pointers, restrict and const restrict pointers, related pointers, thread specific storage, and lots of other things.

    What about English Pointers?

    The way you completely ignore Scotch pointers is racist.

  • (cs) in reply to Steve The Cynic
    Steve The Cynic:
    Tsk. You need to distinguish between Systems HN and Apps HN. The SHN habit of encoding the actual type of the variable in a prefix(1) to its name is indeed a WTF, albeit is relatively mild one.

    (1) Some folks like to use the prefix as the whole name. They deserve to be stabbed repeatedly.

    SHN is a major WTF, because it effectively prevents abstraction from creeping in your code. There are several cases in the Windows API where member of some structure has prefix different from what type it actually is, because at some point they needed to switch to larger type, but couldn't rename the variable, because that would make lot of code not compilable.

    And that's a Windows API, which generally has to be rather stable. In application code changing types (in a compatible way, but breaking SHN) is much more common.

    SHN is also a huge misunderstanding of the Simony's original article.

    Steve The Cynic:
    AHN, on the other hand, encodes useful information about what a variable is contaminated with rather than what its programming language type is.
    This is poor man's variant of what should better be done by actually using the type system. Most widely done in Haskell, but C++ and C# are well suited for this as well as most dynamic languages, actually.
    Steve The Cynic:
    So for example, imagine a system where string variables might contain some sort of raw text, or they might contain some sort of XML-safe or HTML-encoded text, or they might contain SQL-cleansed text (whatever that means. We impose a reasonable rule that a variable contains a maximum of one sort of text, and mark it with the sort of ill the variable is contaminated with:
    std::string rawUserInput; // "raw" indicates raw text
    std::string xsafeProcessedInput; // "xsafe" indicates XML-safe text
    std::string sqlsafeDatabaseReadyInput; // "sqlsafe" indicates SQL-safe text
    
    We also provide conversion functions:
    xsafeProcessedInput = xsafeFromRaw( rawUserInput ); // OK
    xsafeProcessedInput = rawFromXsafe( rawUserInput ); // NOT OK
    Instead, you should have
    std::string UserInput;
    XmlSafeString ProcessedInput;
    SqlSafeString DatabaseReadyInput;
    
    with conversions either implicit or by conversion functions as suitable for particular code.

    In C++ this requires a bit of boilerplate, which can't beat Haskell's

    data XmlSafeString = XmlSafe String deriving (Eq, Ord, Show)
    
    (might have missed some instances to derive), but is well worth it for often used types like this.

    I've actually done this to ensure that all strings presented in the user interface either went through localization machinery or were explicitly marked as not needing to in past project. I've also seen it used for the exact case of proper escaping in a statically typed system in Yesod and in dynamically typed system in Genshi.

    For the example (distinguishing between screen-relative and canvas-relative coordinates) from Simony's original article, Boost.Units can be used to great effect. Since C++11 they even have syntactic support.

    It should be noted that Java is particularly badly suited to these techniques, because it has efficient primitive types with standard operators, but any wrapper has to be class, which is less efficient and can't use the standard operators.

  • MZ (unregistered) in reply to Bulb
    Bulb:
    Instead, you should have
    std::string UserInput;
    XmlSafeString ProcessedInput;
    SqlSafeString DatabaseReadyInput;
    with conversions either implicit or by conversion functions as suitable for particular code.
    This needs to be featured. I thought Spolsky's version of HN made sense, but this is much more powerful.
  • (cs) in reply to Kuba
    Kuba:
    Steve The Cynic:
    faoileag:
    In C++, pointers are occasionally used. Pointers point to memory locations, in other words: are integers.
    Go away and study the history of C and C++ before you spout nonsense like this. On most modern platforms, a plain pointer is a single integer-like value, but there have been exceptions.
    Dude, but this is an implementation detail that's hidden by C++. In C++, no pointer types are default convertible to integers, and no integers are default convertible to pointers except for the literal zero that's taken to mean NULL -- it's a special case. What the underlying platform is doing is completely irrelevant because when you actually write C++ code, pointers don't behave like integers. So please, stop spewing nonsense. The history is irrelevant, what the machine-level implementation does is irrelevant. What matters is that in C++ pointers don't behave like integers, are not integers, and don't mix with integers. If they happen to have the same size as one of the integer types, well, so what, that doesn't make them integers, you know. There's an almost infinite supply of types that can have same size as any other type, you know. The only limitation is due to identifier length restrictions.
    Dude, you need to actually read what I wrote. Hmm, let me clarify that. You need to read what I wrote *after* the first sentence. Only then will you be in a position to accurately critique it.

    The history is not irrelevant - by looking at the history you can see that the concept expressed (pointer != single simple integer) is more than just a theoretical concept, and that there are no (absolute) guarantees about the (relative) sizes of any integer type and pointer types. Nor even that all pointers are the same size. Perhaps your remark was meant to be directed at faoileag, in which case you'd have done better to quote him rather than me.

  • (cs) in reply to Bulb
    Bulb:
    SHN is a major WTF, because it effectively prevents abstraction from creeping in your code. There are several cases in the Windows API where member of some structure has prefix different from what type it actually is, because at some point they needed to switch to larger type, but couldn't rename the variable, because that would make lot of code not compilable.

    And that's a Windows API, which generally has to be rather stable. In application code changing types (in a compatible way, but breaking SHN) is much more common.

    SHN is also a huge misunderstanding of the Simony's original article.

    Actually, much of the Windows API (whether Win16, Win32 or Win64) is a sort of bastard hybrid of SHN and AHN. Consider the number of parameters and structure fields that are 'cbSomething' - this is AHN because the cb prefix shows that this variable is contaminated with the "byte count" property. A window procedure's 'wParam' parameter is notionally a WORD, in the "finest" SHN...

    Bulb:
    Steve The Cynic:
    AHN, on the other hand, encodes useful information about what a variable is contaminated with rather than what its programming language type is.
    This is poor man's variant of what should better be done by actually using the type system. Most widely done in Haskell, but C++ and C# are well suited for this as well as most dynamic languages, actually.
    Yes, provided you're using a language with a suitable type system. AHN has its origins in the days of languages like C, remember.

    My comments were more about what you'd use AHN for, not so much whether it's automatically a good idea, nor if it is necessary with modern systems. I'd agree in general that it's better to use the type system to enforce this, so that you can get the compiler to report the bogus usage rather than the code reviewer.

    But of course Spolsky's article is showing one example of removing the "smell of wrong" about code. In a language whose type system is strong enough to support AHN-by-types rather than AHN-by-names, you concern yourself with other things.

    And the C++ boilerplate you mention can have some not entirely desirable side effects, unless you get into curious perversions like having the base string class support CRTP:

    template <typename DerivedType> class BaseString
    {
      // ...  Normal string operations taking only BaseString<DerivedType> as parameters.
    };
    

    class NormalString : public BaseString<NormalString> { }; class XmlSafeString : public BaseString<XmlSafeString> { }; //etc.

    Note in passing that we need to have somewhere a set of operations/implicit conversions, most likely expressed as pseudo-copy constructor and assignment operations within the derived types, but each of the derived classes inherits the fundamental string operations for itself from the templated base class. This makes them incompatible without introducing the goofy weirdness that is private inheritance.

  • Jibble (unregistered) in reply to Remy Porter
    Remy Porter:
    What makes Hungarian Notation TRWTF, however, is popular it was. This I will never understand. I remember when I was a little n00b programmer, and on my first job, I was taught Hungarian Notation. Even then, I demanded to know, "Why?" The answer always was, "It's the standard. Do it."

    Microsoft used it, everybody followed.

    And the reason Microsoft used it? Because adding a letter or two at the start of every class/variable name avoided name clashes with the existing codebase.

  • Jibble (unregistered) in reply to MZ
    MZ:
    This needs to be featured. I thought Spolsky's version of HN made sense, but this is much more powerful.

    I've lost count of the number of times I've seen that article trotted out as a justification for all sorts of programming practices. It's done untold damage to impressionable young programmers minds in the years since it was written.

    Mind you, Joel is also anti strongly-typed languages. He's shown many times that he simply fails to grok C++. That sort of practice is probably all he has to cling to in his world of scripting languages where variable types can morph right before your eyes.

  • (cs) in reply to Kuba
    Kuba:
    Never mind that std::string is a joke when you actually try to use it in a business application, it misses pretty much everything a real life application needs. QString and friends (or similar classes in other frameworks) is what you need to stay productive.

    What, because we can't do this?

    #include <algorithm>
    #include <boost/algorithm/string.hpp>
    
  • Anon (unregistered) in reply to chubertdev
    chubertdev:
    Anon:
    On Hungarian notation:

    Having 100 variables that all start with int makes intellisense a sad panda.

    Because having all of your variables spread throughout the list instead of grouped together makes more sense.

    I don't search for an int, I search for some useful piece of data. I don't look for all int's and then look for ID, I look for ID (because that's what I need) and then the IDE tells me it's an int.

    If your first step in looking for a particular variable involves looking for the type, then you are doing it wrong.

  • (cs) in reply to Matteo
    Matteo:
    Whatever:
    Call me old-fashion, but I like to know the type of my variable without hovering over it with my mouse. People act like it's the devil, it's just a preference (mostly, depending how you use it).

    Userid = ...well it' a user id, I have no idea what goes in it, but it's a userid

    guidUserID = it's a userid, and it holds a guid....

    I still prefer the latter, you say you don't? Well that's fine too, but why keep hammering on it when it's just preference...

    Until one day, you userid changes representation, and is stored as a long int instead. Then you have to change both type and name of the variable everywhere you use it. And while you might remember, your colleague might not. It's brokenness waiting to happen.

    In most of your functions, the type should be easily inferred from either the formal parameter type, a local declaration. The exception are object members, of course.

    But if you need hungarian notation to clarify your code, there are good chances your code isn't clearly written to begin with. See "Clean Code" of R. C. Martin.

    Finally, instead of "guidUserID", a someone not in love with HN might want to write "userGuid"...

    In any real IDE, right-click and Rename.

    As I've said, your problem is stupid people, not a stupid naming convention.

  • (cs) in reply to chubertdev
    chubertdev:
    Matteo:
    Whatever:
    Call me old-fashion, but I like to know the type of my variable without hovering over it with my mouse. People act like it's the devil, it's just a preference (mostly, depending how you use it).

    Userid = ...well it' a user id, I have no idea what goes in it, but it's a userid

    guidUserID = it's a userid, and it holds a guid....

    I still prefer the latter, you say you don't? Well that's fine too, but why keep hammering on it when it's just preference...

    Until one day, you userid changes representation, and is stored as a long int instead. Then you have to change both type and name of the variable everywhere you use it. And while you might remember, your colleague might not. It's brokenness waiting to happen.

    In most of your functions, the type should be easily inferred from either the formal parameter type, a local declaration. The exception are object members, of course.

    But if you need hungarian notation to clarify your code, there are good chances your code isn't clearly written to begin with. See "Clean Code" of R. C. Martin.

    Finally, instead of "guidUserID", a someone not in love with HN might want to write "userGuid"...

    In any real IDE, right-click and Rename.

    As I've said, your problem is stupid people, not a stupid naming convention.

    Why use some sad movable thing with generally less than 5 buttons when you got another thing with about 100 buttons that you don't have to move around?

  • Captain Oblivious (unregistered) in reply to MZ
    MZ:
    Bulb:
    Instead, you should have
    std::string UserInput;
    XmlSafeString ProcessedInput;
    SqlSafeString DatabaseReadyInput;
    with conversions either implicit or by conversion functions as suitable for particular code.
    This needs to be featured. I thought Spolsky's version of HN made sense, but this is much more powerful.

    I think you would like Haskell. These kinds of constructs are built in, at a few levels of abstraction.

    For example, we can create so-called newtypes to create a new type from a type:

    newtype UserInput = UserInput { unUserInput :: Text } deriving (Eq, Ord, Show)
    newtype XmlSafe = XmlSafe { unXmlSafe :: Text }  deriving (Eq, Ord, Show)
    

    This code uses a few other Hungarian-like conventions I like. The 'un's are a simple way to remember how to deconstruct a newtype, for example.

    We can also abstract from these into newtypes with free type variables:

    newtype SqlSafe a = SqlSafe { unSqlSafe :: a } deriving (Eq, Ord, Show)

    We can use these for situations when many different things can be made SqlSafe, for example, by parsing and validating:

    sqlTextUserInput :: UserInput -> SqlSafe Text
    sqlStrUserInput :: UserInput -> SqlSafe String

    The notation is starting to grow unwieldly, though. We can use a final type class to deal with dispatch in a type-safe way:

    class SafeSql a where parseUserInput :: UserInput -> SafeSql a
    instance SafeSql Text where parseUserInput = ...
    instance SafeSql String where ...
    

    Also, those newtypes with free type variables are useful for another reason. We can thread computation through the free variable. That takes us into the realm of functors, applicatives, and monads, which I won't go into. But, it's a nice topic which deserves some motivation. We might want to construct SqlSafe queries. If we use the applicative interface, we can write composable getters like so:

    parseUserName :: SqlSafe Name
    parseUserId :: SqlSafe UserId
    
    parseUser :: UserInput -> SqlSafe User
    parseUser i = User <$> parseUserName i 
                       <*> parseUserId i
    

    Also, in writing this example, I felt the pull to abstract the SafeSql type class to accept multiple parameters, so that we can parse more than just UserInput types into SqlSafe types. Something like:

    class SafeSql a b where parse :: a -> SqlSafe b

    In this way, we can generate SQL or whatever using data from many sources. For example, User Input, Configuration, the results of SqlSafe actions. Indeed, if we restructure SqlSafe slightly, so that it carries around more information, we can use the monad type class method:

    join :: (Monad m) => m (m a) -> m a

    to compute Sql joins! (I would suggest this should be done by modifying and issuing a single query rather than issuing many queries and calculating the join in code...) That's actually pretty neat, since it demonstrates that SQL joins are joins on the lattice of relations.

  • (cs) in reply to Matt Westwood
    Matt Westwood:
    The way you completely ignore Scotch pointers is racist.

    Sounds like a drink that's half Scotch, half Fireball.

  • (cs) in reply to Tractor
    Tractor:
    Why use some sad movable thing with generally less than 5 buttons when you got another thing with about 100 buttons that you don't have to move around?

    I think that I'm misunderstanding you. Is the "sad movable thing" the stupid person that I'm talking about?

  • (cs) in reply to Anon
    Anon:
    chubertdev:
    Anon:
    On Hungarian notation:

    Having 100 variables that all start with int makes intellisense a sad panda.

    Because having all of your variables spread throughout the list instead of grouped together makes more sense.

    I don't search for an int, I search for some useful piece of data. I don't look for all int's and then look for ID, I look for ID (because that's what I need) and then the IDE tells me it's an int.

    If your first step in looking for a particular variable involves looking for the type, then you are doing it wrong.

    "Did I call it DateOfYellingAtUser or YellingAtUserDate...I guess I'll have to try both, or just scroll a lot..."

  • Joe (unregistered) in reply to faoileag

    Pointers aren't always plain addresses, even in modern architectures. Function pointers are most likely these days to run afoul of such assumptions.

  • Aesop (unregistered)

    AN IDLE HORSE, and an Ass laboring under a heavy burden, were traveling the road together.

    The Ass, ready to faint under his heavy load, entreated the Horse to assist him, and lighten his burden, by taking some of it upon his back.

    The Horse was ill-natured and refused to do it; upon which the poor Ass tumbled down in the midst of the highway, and expired.

    The countryman then took the whole burden, and laid it upon the Horse, together with the skin of the dead Ass.

  • Joe (unregistered) in reply to Jibble
    Microsoft used it, everybody followed.

    And the reason Microsoft used it? Because adding a letter or two at the start of every class/variable name avoided name clashes with the existing codebase.

    Well, also, they initially programmed much of Windows in assembly code, and the type information is actually useful in that environment.

  • urza9814 (unregistered) in reply to Gene Wirchenko
    Gene Wirchenko:
    1) So what *does* "CVS" stand for? GIYF unless there are many results returned. There were.
    1. As to Hungarian Notation, I find it useful. Some of the prefixes that I use are: f for flag. fClient means Is this a client? c for count. cClient means How many clients? s for size. h for handle, fh for file handle. x for index/offset.

    I find HN useful if I am dealing in more than one representation. e.g. iSomething is an integer, and strSomething is the string form.

    Sincerely,

    Gene Wirchenko

    Is it REALLY that hard to take the extra half second to type clientFlag or clientCount? Or are you like half these people I work with who are somehow programmers when they can't even type properly?

  • Captain Oblivious (unregistered) in reply to Matteo
    Matteo:
    Whatever:
    Call me old-fashion, but I like to know the type of my variable without hovering over it with my mouse. People act like it's the devil, it's just a preference (mostly, depending how you use it).

    Userid = ...well it' a user id, I have no idea what goes in it, but it's a userid

    guidUserID = it's a userid, and it holds a guid....

    I still prefer the latter, you say you don't? Well that's fine too, but why keep hammering on it when it's just preference...

    Until one day, you userid changes representation, and is stored as a long int instead. Then you have to change both type and name of the variable everywhere you use it. And while you might remember, your colleague might not. It's brokenness waiting to happen.

    This is why type safety was invented about 50 years ago.

  • All hail the Google (unregistered) in reply to Kuba
    Kuba:
    PAul M:
    Rule 2 is: never use floating point except for physical measurements accepted to be approximations or for statistical averages. In particular, never use it for units of (billable) time or for currency. Ever. Store these quantities as integer multiples of the smallest division you will need.

    Had one project which kept track of hours. Then a requirement came to track half hours, so they made time a float. Dear God, you would not believe the mess …

    Given that a double is perfectly fit for use as an equivalent of a 53 bit 2s complement integer, I don't quite see what the problem is. Heck, if all you ever do is add half hours and no other fractions, it'll still work just fine and will provide you with exact results if the rounding mode is correctly set. Comparisons will work normally as well. You run into obvious problems when you try to use floating point for numbers that don't have a finite representation that fits into the bits you allocate for the fraction in the mantissa. Decimal fractions of any length are not representable in binary floating point and thus you definitely do not want to, say, store currency amounts as-is in floating point. You can store them once you multiply them by 100 so that they become representable in floating point. I had a lot of code that ran on 286 machines where I needed reasonably performing 32 bit integers. The double type was perfect for that purpose, and the math coprocessor did its job as it was designed to.

    Many decimal fractions can be stored just fine. I'm surprised no one else corrected you. As an example, we can store 2.5 (dec) just fine in floating point (0x40200000). Only some numbers are endlessly repeating fractions. If floating point couldn't represent any fraction usefully, why would it even exist?

  • AN AMAZING CODER (unregistered) in reply to Remy Porter
    Remy Porter:
    Yes. Hungarian Notation is, and always has been, a WTF. If you're using a strongly typed language, it means that you don't trust the compiler or aren't prepared to deal with compiler errors. If you're using a weakly typed language, it demonstrates that you're trying to enforce strong-typing-by-convention- which doesn't work. It provides a false sense of type safety ("the variable is named strFoo, it must hold a string" is a statement of hope, not reality).

    In the name of being "self-documenting", it favors knowing what prefixes mean over knowing what the code does ("Oh, gStrFoo", it's a global, so I know to be careful about touching it. I don't know what or how this variable is used, but I know it's global! Self documenting!")

    What makes Hungarian Notation TRWTF, however, is popular it was. This I will never understand. I remember when I was a little n00b programmer, and on my first job, I was taught Hungarian Notation. Even then, I demanded to know, "Why?" The answer always was, "It's the standard. Do it."

    The hungarian notation you speak of isn't the actual hungarian notation, it's the wrong way that somehow became the popular way.

Leave a comment on “Do Not Remove Complete!”

Log In or post as a guest

Replying to comment #:

« Return to Article