Thread: another mystifying C syntax issue - DEBUG macro - #define

  1. #1
    Registered User
    Join Date
    Feb 2012
    Posts
    44

    another mystifying C syntax issue - DEBUG macro - #define

    Simply unbelievable.
    Code:
    #define DEBUG
    #ifdef DEBUG
    #define dg(x) _tprintf(x)
    #else
    #define dg(x) do {} while (0)
    #endif
    


    dg ( (_T("parsed node %s %d"), wordwrk, z) );

    The above fails because the LAST _tprintf parameter is an INT. Swap the two and the syntax is then OK:

    dg ( (_T("parsed node %d %s"), z, wordwrk) );

    I'm all ears as to why this simply isn't a replacement of dg with _tprintf.



    Fortunately, I found another better use of macros for debugging:

    Code:
    #define NODEBUG2
    #ifdef DEBUG2
    #define D if(1)
    #else
    #define D if(0)
    #endif
    
    D _tprintf(_T(parsed node %s %d"), wordwrk, z);


    Last edited by jlewand; 02-12-2012 at 12:45 PM.

  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    All ears, no brain. If you'd simply learn C you'd know about the comma operator and wouldn't be so mystified.

    The result of your macros is:
    Code:
    _tprintf(("parsed node %s %d", wordwrk, z));
    I.e. you're passing to _tprintf a single parameter that uses the comma operator. The comma operator processes each comma separated statement in order and results in only the last one. So if your last one is a string, that will compile since _tprintf expects a string as its first parameter. If the last is an int, that won't compile. But even in the case where the string is the last one, it's not doing what you want it to.

  3. #3
    Registered User
    Join Date
    Feb 2012
    Posts
    44
    I've googled C syntax for near a month now. It's very, very cryptic. Remember, I've programmed assembly for 30+ years as well as a half dozen other languages. Nothing remotely compares to C syntax in obtuseness. It's simply hit or miss. I've written assemblers for microprocessors and even commercial software.

    I was simply using what others have used (this specific Debug macro). I am now using the D one above. I had the misfortune of calling D, DBG, and that caused some severe compiler error in objbase.h whereby it wouldn't compile the program at all.

    I did test this and even looked at the generated code to make sure it was doing what I expected it to. However, for the macro expansion, there is one extra PUSH statement over the native _tprintf that I haven't figured out why it's there (yet).

    Also, where did the _T part go in your above macro result?
    Last edited by jlewand; 02-12-2012 at 01:42 PM.

  4. #4
    Registered User
    Join Date
    Dec 2011
    Posts
    795
    Quote Originally Posted by jlewand View Post
    I've googled C syntax for near a month now. It's very, very cryptic.
    That was the most ignorant thing I've ever heard said about C syntax. It's not "cryptic", it's actually very logical. Just because you cannot figure it out does not mean that you can call out one of the most solid and popular programming languages.

    Quote Originally Posted by jlewand View Post
    Remember, I've programmed assembly for 30+ years as well as a half dozen other languages.
    Then you should know a lot more about C than what you already do. C syntax is very similar to many other languages, and assembly should teach you tons about low-level control, logic, and actually a lot more.

    Quote Originally Posted by jlewand View Post
    Nothing remotely compares to C syntax in obtuseness. It's simply hit or miss.
    Really? Are you learning C by studying good source code, or are you shoving together random symbols until they vaguely look like what you've seen before. C is not at fault, your brute-force programming style is.

  5. #5
    Registered User
    Join Date
    Feb 2012
    Posts
    44
    C syntax is very, very cryptic compared to ALL those other languages I've been exposed to over the past 30+ years. It is difficult to figure out because it is so cryptic. C syntax is unlike all other languages and assembler instruction sets I've been exposed to.

    Knowing assembler is virtually useless to understanding C syntax. I'm also at a disadvantage as only 1 of the processors I've worked with had a stack (the 6502). So, immersing myself in how C handles functions (variable addressing) has been interesting.

    In my field of large-scale computer performance engineering, I'm now understanding why memory usage is so bizarre in C-derivative languages (ETL tools, Java, etc.) on tier 2. I've been involved so long on a platform where there is zero memory management provided by the system except (obviously) at task/process shutdown. Hence, I don't even think about the things that come into play re: C.

    I have no brute-force programming. If you ever saw my (non-C) code it is a piece of art. I'd be glad to email you some examples.

    For example, assembler programming has very few rules. A load instruction will load the operand address into a register regardless of whether you deem it "character" or binary or whatever.

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    To be fair, the finer points of macro substitutions are not something the average newbie would run into.

    I also think the first declaration is wrong anyway, and it should have been this.
    #define dg(x) _tprintf x


    So when you did this
    dg ( (_T("parsed node %s %d"), wordwrk, z) );
    the single parameter (the parentheses protect the inner commas from being macro parameter separators), you end up with

    _tprintf (_T("parsed node %s %d"), wordwrk, z);
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Dec 2011
    Posts
    795
    Quote Originally Posted by jlewand View Post
    In my field of large-scale computer performance engineering, I'm now understanding why memory usage is so bizarre in C-derivative languages (ETL tools, Java, etc.) on tier 2. I've been involved so long on a platform where there is zero memory management provided by the system except (obviously) at task/process shutdown.
    I don't see how memory management is bizarre in C. Almost everything involving the heap is done manually, and while the stack uses abstractions (no push/pop, variables, etc. ), it doesn't necessarily do memory management for you.

    Quote Originally Posted by jlewand View Post
    For example, assembler programming has very few rules. A load instruction will load the operand address into a register regardless of whether you deem it "character" or binary or whatever.
    And so will C.

    Code:
    char a = 100;
    char a = 0x100;
    
    __asm__ (
    "movl $100, %%eax"
    "movl $0x100, %%eax"
    );
    Both examples, you're specifying the type of data that goes into the register. And, a load instruction won't necessarily load the address into the register, nor will C load any type into a variable.

  8. #8
    Registered User
    Join Date
    Feb 2012
    Posts
    44
    Thanks Salem. So, are you saying your
    _tprintf x

    will work but my original

    _tprintf(x)

    will not work as expected?

  9. #9
    Registered User
    Join Date
    Feb 2012
    Posts
    44
    Quote Originally Posted by memcpy View Post
    I don't see how memory management is bizarre in C. Almost everything involving the heap is done manually, and while the stack uses abstractions (no push/pop, variables, etc. ), it doesn't necessarily do memory management for you.



    And so will C.

    Code:
    char a = 100;
    char a = 0x100;
    
    __asm__ (
    "movl $100, %%eax"
    "movl $0x100, %%eax"
    );
    Both examples, you're specifying the type of data that goes into the register. And, a load instruction won't necessarily load the address into the register, nor will C load any type into a variable.
    The concept of recursion and, by proxy, the stack will take some mental gymnastics to deprogram myself from 30 years of programming. It's not intuitive, yet, as far as being able to crank out some cool function that uses it. It will take some time to assimilate all of this.

    Again, simple things like, when you call a function, the local variables in that function are no longer available once that function ends. It's "obvious" that's how C works, but it's not intuitive as the whole concept of recursion is so alien a concept that I've never even thought about it. With that said, years ago, in a program I was writing that had a lot of parsing in it, I actually wrote a subroutine to handle recursion (return register stack array I had to build manually).

    Also, after seeing the instruction set for the x86 family (as your note above), MOVING is NEVER EVER used as a mnemonic for anything that involves a register in ANY instruction set I've worked with. That doesn't make the x86 wrong, but unlike all others I'm familiar with. You LOAD and STORE registers, you don't "move" things to them. Just another major adjustment. It's far easier to read an assembler listing that when you see a MOVx mnemonic, you KNOW it's not changing the contents of any registers.

    Friday at work, I tried with all my might to use a void *pointer to see if I could walk storage. Not to occur. As with all high-level languages, you fight the hurdles that said language puts in front of you. In assembler, other than privileged vs. non-privileged instructions, you can do anything you want. No restrictions (yes, still memory key access stuff).

    The good news is, I'm almost done with my first C program ever (3000 lines or so). Again, not to editorialize, but I had one class in college in 1982 that had C as its basis. When I first saw the language, I actually thought it was some kind of home-grown language used for class (e.g. it wasn't really a used language in the real world). Some languages just strike you as very appealing, others not so. C-derivative languages are in that family for me.
    Last edited by jlewand; 02-12-2012 at 10:31 PM.

  10. #10
    Registered User
    Join Date
    Dec 2011
    Posts
    795
    Quote Originally Posted by jlewand View Post
    Again, simple things like, when you call a function, the local variables in that function are no longer available once that function ends. It's "obvious" that's how C works, but it's not intuitive as the whole concept of recursion is so alien a concept that I've never even thought about it. With that said, years ago, in a program I was writing that had a lot of parsing in it, I actually wrote a subroutine to handle recursion (return register stack array I had to build manually).
    Actually, that's a good point, I really hadn't thought about that.

    Quote Originally Posted by jlewand View Post
    Also, after seeing the instruction set for the x86 family (as your note above), MOVING is NEVER EVER used as a mnemonic for anything that involves a register in ANY instruction set I've worked with. That doesn't make the x86 wrong, but unlike all others I'm familiar with. You LOAD and STORE registers, you don't "move" things to them. Just another major adjustment. It's far easier to read an assembler listing that when you see a MOVx mnemonic, you KNOW it's not changing the contents of any registers.
    MOVx does change the contents of one of the registers

    Either way, the same logic should apply to C: different syntax, similar result/ends. Obviously it's a much greater difference in both method and syntax, so it'll take a lot of learning, but it's worth it.


    Quote Originally Posted by jlewand View Post
    Friday at work, I tried with all my might to use a void *pointer to see if I could walk storage. Not to occur. As with all high-level languages, you fight the hurdles that said language puts in front of you. In assembler, other than privileged vs. non-privileged instructions, you can do anything you want. No restrictions (yes, still memory key access stuff).
    Partially true, and partially not. I wouldn't say C is a high-level language (definitely higher than asm), but it still allows you to do anything you could do in asm. For example, arithmetic on a void pointer doesn't work because C doesn't know how much to increment it by; whereas asm would increment it by the logical count of one. And, to your credit, it isn't intuitive that a char array would be a simple area of one-byte blocks, but, if pointer arithmetic is needed, it's incredibly easy to do.

  11. #11
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by memcpy View Post
    but it still allows you to do anything you could do in asm.
    Actually it does not, why else would there be a need for a feature to inline assembly (or call asm macros) in the C code.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by jlewand
    C syntax is very, very cryptic compared to ALL those other languages I've been exposed to over the past 30+ years.
    Quote Originally Posted by jlewand
    Again, simple things like, when you call a function, the local variables in that function are no longer available once that function ends. It's "obvious" that's how C works, but it's not intuitive as the whole concept of recursion is so alien a concept that I've never even thought about it.
    Out of curiosity, but what programming languages have you been exposed to over the past 30+ years? I find it hard to imagine that a programmer with as much experience as you would find recursion to be an alien concept when it has been a staple of various programming languages for decades, presumably even before you wrote your first program, notably the Lisp family before C was invented. Likewise, I am hard pressed to think of a high level programming language in common use that does not have a concept of scope for variables local to a function, even if they differ in the details. Consequently, it sounds like you are experiencing "culture shock" at the different paradigms of a higher level programming language, rather than because C is cryptic (although it is cryptic in some ways, but then, any programming language that is not a "natural language" is cryptic to some extent).
    Last edited by laserlight; 02-12-2012 at 11:11 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by Subsonics View Post
    Actually it does not, why else would there be a need for a feature to inline assembly (or call asm macros) in the C code.
    Indeed it does not quite. For example many processors include a bit-rotate command whereas C only has bit-shifts. There's also SIMD type of instructions.
    Note that a lot of this is do-able in plain C now with the help of many intrinsics supported by various compilers.

    And yes C is somewhat cryptic. It's more cryptic than Pascal for example, but there are certainly far more cryptic languages out there also.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  14. #14
    Registered User ledow's Avatar
    Join Date
    Dec 2011
    Posts
    435
    I would regard this as one of those problems that's solved by using the right tools. In this case, the right tools would be an IDE that can expand your macros for you, which is very helpful when it comes to spotting things like this.

    My IDE (Eclipse CDT) actually gives an instant "syntax error" on the left-hand-side when you try to use the macro in the way you did. It doesn't error the macro (because there's nothing syntactically wrong with it) but the invocation line. Removing the extraneous brackets which are easily visible in the macro expansion (generated every time you hover over the macro name within the code) stops the syntax error immediately.

    Macros are an odd feature of C. The preprocessor really is a totally separate program and language. This is why I used to hate C when I first tried it - I "had" to learn three languages: C, C pre-processor and Makefiles (dependent on the architecture - I used batch files and shell scripts for compilation for years and still do sometimes). To be honest, I consider that the biggest barrier to entry when it comes to C programming - the language is well-defined, but gathering the tools, configuring library paths, obtaining libraries, preparing Makefiles, learning gcc-isms, etc. is a huge hurdle to even a simple Hello World. That's before you even get close to learning various dialects of the language (C99, GNU-C99, C++, etc.).

    The preprocessor is dumb. If you put in an extraneous bracket it will leave it in and even propagate it. When that then comes out to code that you'd *never* write (i.e. even an extra bracket), you can cause a lot more trouble for yourself than necessary. Treat macros as dumb objects - they are nothing more than string manipulations. If that string comes out to something that you *wouldn't* write, in bracketing, spacing, etc., then fix it before you carry on. The preprocessor, used properly, is quite capable of making a single extraneous character break thousands of lines of otherwise valid code because of the way it expands everything that uses it in all the files it's used in. Debugging that is not always easy which, in programmer terms, means you need to spend extra effort to make sure it's bug-free. Hell, even a missing bracket can cause just as many problems too (so you can't just think "I'll use less brackets").

    Get an IDE. Get one that uses macro expansion and highlights syntax errors (either on-the-fly like Eclipse, or after each compilation) and has a very good debugger (because the assembler generated by that statement actually shows you what's going on even if you've never seen the "comma" operator that the first reply talks about). Use it, and never let a warning pass you by.

    - Compiler warnings are like "Bridge Out Ahead" warnings. DON'T just ignore them.
    - A compiler error is something SO stupid that the compiler genuinely can't carry on with its job. A compiler warning is the compiler saying "Well, that's bloody stupid but if you WANT to ignore me..." and carrying on.
    - The best debugging tool in the world is a bunch of printf()'s for everything important around the bits you think might be wrong.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How overload a define macro?
    By 6tr6tr in forum C++ Programming
    Replies: 19
    Last Post: 04-24-2008, 03:52 PM
  2. Is it possible to set type for define macro?
    By 6tr6tr in forum C++ Programming
    Replies: 5
    Last Post: 04-16-2008, 01:32 PM
  3. Newbie question about the define macro
    By lawina in forum C++ Programming
    Replies: 11
    Last Post: 05-18-2006, 06:47 PM
  4. Tricky #define macro...
    By willkoh in forum Windows Programming
    Replies: 4
    Last Post: 04-06-2005, 12:09 PM
  5. macro (#define) question
    By Darrok in forum C++ Programming
    Replies: 30
    Last Post: 12-20-2001, 05:01 PM