Generic programming in C



  • The wonders of generic programming in C. I can't tell if the guy who wrote this is coming from an assembly-language background or an object-oriented background.

    /* EqualStruct compares one struct to another, returns TRUE if they match
    exactly.  Note that structs must be an even byte count for this to work! */
    
    PASCAL (my_boolean) EqualStruct (void FAR *rec1, void FAR *rec2, long length)
    {
    	register short FAR		*ptr1;
    	register short FAR		*ptr2;
    	register long		ctr;
    	
    	for (ptr1 = (short FAR*)rec1, ptr2 = (short FAR*)rec2, ctr = length / sizeof(short); ctr; --ctr)
    		if (*ptr1++ != *ptr2++)
    			return	FALSE;
    		
    	return	TRUE;
    }
    


  • far pointers?  *weeps*

     

    And what is this "PASCAL (my_boolean)" nonsense?



  • @Carnildo said:

    [code]Note that structs must be an even byte count for this to work![/code]
    I'm sure this has led to plenty other hillarities, as well.



  • @morbiuswilters said:

    far pointers?  weeps

     

    And what is this "PASCAL (my_boolean)" nonsense?

    Platform independance. In theory, FAR will be #defined as "__far" when compiling for Win16 (we haven't targeted that platform for a decade) and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else. In practice, it's just a pain in the ass: the PASCAL macro breaks Xcode's code-navigation, while FAR is just so much noise.



  • @Carnildo said:

    @morbiuswilters said:

    far pointers?  *weeps*

     

    And what is this "PASCAL (my_boolean)" nonsense?

    Platform independance. In theory, FAR will be #defined as "__far" when compiling for Win16 (we haven't targeted that platform for a decade) and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else. In practice, it's just a pain in the ass: the PASCAL macro breaks Xcode's code-navigation, while FAR is just so much noise.

    Ah, okay.  I wondered why it was all-caps but figured it was just a define for clarity, not compatiblity.



  • Definately assembly background.

    It's curious that someone that knows C can be unfamiliar with the memcmp() function that has come with every C runtime library that has ever shipped.  It even (gasp!) handles odd byte counts, although I don't think you will find many odd sized structures for code targetted at modern CPUs.


  • BINNED

    @LoztInSpace said:

    memcmp() [...] even (gasp!) handles odd byte counts.

     

    He could have just used char* instead of short* when reinventing the square wheel.



  • @LoztInSpace said:

    Definately assembly background.

    It's curious that someone that knows C can be unfamiliar with the memcmp() function that has come with every C runtime library that has ever shipped.  It even (gasp!) handles odd byte counts, although I don't think you will find many odd sized structures for code targetted at modern CPUs.

    This code uses "FAR" and "PASCAL"; it's not exactly "targeted at modern CPUs".  Ironically, that means it's as more likely to encounter odd-sized structures, as it is less able to deal with them correctly ... 



  • @topspin said:

    He could have just used char* instead of short* when reinventing the square wheel.

    But that would have been less efficient! I suspect an assembler background too... 


  • Discourse touched me in a no-no place

    @topspin said:

    @LoztInSpace said:

    memcmp() [...] even (gasp!) handles odd byte counts.

     

    He could have just used char* instead of short* when reinventing the square wheel.

    But that's twice as many comparisons!!!one!

     Assuming that sizeof(short) is two, which, of course, isn't necessarily a given, making the comment wrong. (It can be larger.)



  • @PJH said:

     Assuming that sizeof(short) is two, which, of course, isn't necessarily a given, making the comment wrong. (It can be larger.)

    Or smaller.



  •  Assembly background... I have no doubt about it.

     

    IIRC, the primary use of the PASCAL macro was to have the compiler use a Pascal byte order and method of passing parameters so the function could be called from Pascal-based languages.  It was pretty common in the 90's.

     

    The use of the 'register' keyword is also a good clue for the age of the code.



  •  @r3jjs said:

    The use of the 'register' keyword is also a good clue for the age of the code.

    Care to explain? Alot of us still use the register keyword today, since it's a nice compiler optimizer hint (like const, restrict, etc)


  • Discourse touched me in a no-no place

    @bitpirate said:

    Care to explain? Alot of us still use the register keyword today, since it's a nice compiler optimizer hint
    .. which by and large, goes ignored these days (the compiler is much better at deciding which variables should be stored in registers,) and means you can't access the variable by address if you need to in C.



  • @LoztInSpace said:

    Definately assembly background.

    It's curious that someone that knows C can be unfamiliar with the memcmp() function that has come with every C runtime library that has ever shipped.  It even (gasp!) handles odd byte counts, although I don't think you will find many odd sized structures for code targetted at modern CPUs.

    And with any compiler worth considering I would expect memcmp() to be at worst exactly the same speed as this version, probably faster.


  • @Carnildo said:

    @morbiuswilters said:

    far pointers?  weeps

     

    And what is this "PASCAL (my_boolean)" nonsense?

    Platform independance. In theory, FAR will be #defined as "__far" when compiling for Win16 (we haven't targeted that platform for a decade) and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else. In practice, it's just a pain in the ass: the PASCAL macro breaks Xcode's code-navigation, while FAR is just so much noise.

    Why have the PASCAL macro take the return type as an argument though? Why not just my_boolean PASCAL func(...)?



  • @Carnildo said:

    ...and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else.

    I'm pretty sure that "PASCAL" works just like "WINAPI" when used with the appropriate Microsoft Windows headers (ie. it means __stdcall for Win32), I even recall seeing PASCAL in some of the sample MSDN code from around 2000-2002.



  • @tdb said:

    @Carnildo said:
    @morbiuswilters said:

    far pointers?  weeps

     

    And what is this "PASCAL (my_boolean)" nonsense?

    Platform independance. In theory, FAR will be #defined as "__far" when compiling for Win16 (we haven't targeted that platform for a decade) and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else. In practice, it's just a pain in the ass: the PASCAL macro breaks Xcode's code-navigation, while FAR is just so much noise.

    Why have the PASCAL macro take the return type as an argument though? Why not just my_boolean PASCAL func(...)?
    NB: There is a space between PASCAL and the "(" for a reason, "my_boolean" is not an argument for the macro!



  • @nat42 said:

    @tdb said:
    @Carnildo said:
    @morbiuswilters said:

    far pointers?  weeps

     

    And what is this "PASCAL (my_boolean)" nonsense?

    Platform independance. In theory, FAR will be #defined as "__far" when compiling for Win16 (we haven't targeted that platform for a decade) and PASCAL(my_boolean) is a macro that expands to "my_boolean pascal" or "my_boolean __pascal" on platforms that support it, while expanding to "my_boolean" on everything else. In practice, it's just a pain in the ass: the PASCAL macro breaks Xcode's code-navigation, while FAR is just so much noise.

    Why have the PASCAL macro take the return type as an argument though? Why not just my_boolean PASCAL func(...)?
    NB: There is a space between PASCAL and the "(" for a reason, "my_boolean" is not an argument for the macro!
    Right, my bad. I use the C preprocessor so little (C++ templates and inline functions are far better) that I don't remember all the nitty-gritty details about its syntax.



  • I demand the definition of my_boolean.



  • @Spectre said:

    I demand the definition of my_boolean.
    #define my_boolean FILE_NOT_FOUND



  • Just found this function in the same codebase. From the header comments, it was written about three years later.

    /* EqualStruct_1b compares one struct to another, returns TRUE if they match
    exactly.  This one works for an odd number of bytes!*/
    
    PASCAL (my_boolean) EqualStruct_1b (void FAR *rec1, void FAR *rec2, long length)
    {
    	register char FAR		*ptr1;
    	register char FAR		*ptr2;
    	register long				ctr;
    	
    	for (ptr1 = (char FAR *)rec1, ptr2 = (char FAR *)rec2, ctr = length; ctr; --ctr)
    		if (*ptr1++ != *ptr2++)
    			return	FALSE;
    		
    	return	TRUE;
    }
    


  • Wouldn't this not only work for evens as well, but woudln't it have sufficed for the original in the first place?

    <font color="#ffffff">(yeah yeah, refactoring anyways) </font>


  • Discourse touched me in a no-no place

    @drachenstern said:

    Wouldn't this not only work for evens as well, but woudln't it have sufficed for the original in the first place?
    Ignoring the fact it's just reinventing a square wheel - it's re-implementing a standard library function that's, at best the same speed, but in all probability faster, yes.
    @drachenstern said:
    (yeah yeah, refactoring anyways)
    Just replace the bodies of both functions with a call to memcmp().



  •  But an exact byte-for-byte match may not be what you want either. Consider the following:

    #pragma pack(4) /* for clarity */
    typedef struct
    {
      char a;
      int b;
    } MyStruct;


    int main(int argc, char* argv[])
    {
      MyStruct s1;
      MyStruct s2;

      *(int*)(&s1) = 0x55555561;
      s1.b = 42;

      *(int*)(&s2) = 0xaaaaaa61;
      s2.b = 42;

      printf("s1.a = %c, s1.b = %d\ns2.a = %c, s2.b = %d\n", s1.a, s1.b, s2.a, s2.b );
      printf("memcmp(&s1, &s2, sizeof(MyStruct)): %d\n", memcmp(&s1, &s2, sizeof(MyStruct)));
      return 0;
    }

    s1 and s2 are really the same, but with different bits in them.


Log in to reply