Recent

Author Topic: Does Freepascal has everything "must have" functional features as C does  (Read 3126 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 5563
Discussion of assembler-level hacks is not relevant to OP's question. Neither language supports this sort of thing directly, implementations of both languages typically allow linkage to assembler routines.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

y.ivanov

  • Hero Member
  • *****
  • Posts: 554
Yes, like you can change the return address of your function by making a local string buffer and copying one in there that is longer. We call that a buffer overrun. This, too, can be used on micro-controllers to jump from one thread into another one.

Or, you could make a function pointer and insert the address you want, although you could do that in Pascal as well, so that's not an argument, either.

Like, on some micro-controllers, you can use the code as constants, to save on storage space. Or overlap code so you have two different functions, depending on the entry-point chosen. Re-use threads by saving and restoring only part of the registers. Compress and decompress things. Use bits in code words that aren't used for the opcode as flags. Etc. But if you want to do any of those things, you probably don't want the overhead of a runtime and use assembly.
My sole point was to give to marcov a case when the C switch/case behavior can be of real use.
The protothreads are Duff's device inspired, despite the device is actually a manual loop unrolling.
IMO discussing concurrent programming techniques is out of topic. 
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MarkMLl

  • Hero Member
  • *****
  • Posts: 5563
IMO discussing concurrent programming techniques is out of topic.

Noting of course that FPC includes facilities for supporting threads, and even if TThread itself is part of the RTL rather than the language that is arguably something in its favour.

But coroutines and assembler-level hacks are definitely OT.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10395
  • FPC developer.
My sole point was to give to marcov a case when the C switch/case behavior can be of real use.

Duff's device originally also was. You are missing the point of the argument, the fact that you can abuse it for hackish things (and hopefully not touch implementation defined behaviour along the way) doesn't excuse that it was not intended for such purposes.

As said, if fall-through was intended, rather than an attempt to save on syntax for the multiple clause case, then there would be two switch/case statements.

(

     
Code: [Select]
       case 1:
       case 2:
                   bla;
                   break;
   

vs

   
Code: [Select]
    case 1,2 :  bla;
   
)

PascalDragon

  • Hero Member
  • *****
  • Posts: 4744
  • Compiler Developer
As long for an embedded, bare metal firmware, I'm using C with an approach similar to the protothreads. It exploits the C switch/case behavior (like in the Duff's device) and parametrized macros to achieve stackless multi-threading.
The lack of similar preprocessor and the different case statement makes such a technique infeasible in FPC.

It is true that with the help of fibers a lightweight co-routines can be implemented, but they're in no way stackless. It's about microcontrollers with a tiny amount of resources, where the protothreads can be the only solution.

I think you can do that sort of thing up to a point using SetJmp()/LongJmp(), but the number of issues that somebody highlighted earlier in the year relating to exceptions etc. makes that tenuous.

Considering the use case mentioned by y.ivanov this wouldn't be relevant anyway as you wouldn't use Object Pascal exception handling on such a target.

y.ivanov

  • Hero Member
  • *****
  • Posts: 554
*snip*
As said, if fall-through was intended, rather than an attempt to save on syntax for the multiple clause case, then there would be two switch/case statements.
*snip*
Without being an advocate of neither language, i should say that the C switch/case and Pascal case statements are significantly different in their meaning (although most of the time it doesn't look so) with the former being a form of parametrized goto and the latter being a true alternatives selection.
The C case labels can appear everywhere, incl. in the middle of a nested block:
Code: C  [Select][+][-]
  1. // taken from https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
  2. int function(void) {
  3.     static int i, state = 0;
  4.     switch (state) {
  5.         case 0: /* start of function */
  6.         for (i = 0; i < 10; i++) {
  7.             state = 1; /* so we will come back to "case 1" */
  8.             return i;
  9.             case 1:; /* resume control straight after the return */
  10.         }
  11.     }
  12. }
Which is absolutely unacceptable in Pascal (that feature is also exploited into the Duff's device, not only the fall-through).

Considering that (significant) difference, it is no wonder that something written with switch/case in C can't be achieved with the Pascal's case statement and vice versa.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1131
Here you go.

Code: Pascal  [Select][+][-]
  1. function Test: Boolean;
  2. var
  3.   i: Integer;
  4. label
  5.   Start, Loop, Stop;
  6. begin
  7. Start:
  8.   i := 0;
  9. Loop:
  10.   Inc(i);
  11.   if i >= 1000 then Goto Stop;
  12.   Goto Loop;
  13. Stop:
  14.   Result := i;
  15.   if Random(10) = 0 then Goto Start;
  16. end;

MarkMLl

  • Hero Member
  • *****
  • Posts: 5563
Here you go.

Yes /but/: everybody agrees that C syntax is easily abused, and that Pascal can, by and large, achieve the same result using better program structure. However I introduced Duff's Device into this thread with the intention of refuting the unqualified "Pascal is a superset of C" assertion: there's lots of languages which /are/ supersets of C and which strive for backward compatibility but Pascal isn't one of them.

Leaving aside library differences, I think we collectively identified two features that C has where there is no direct Pascal equivalent: the ternary conditional and parameterised macros. Everything else is just noise.

At least nobody introduced Turing equivalence...

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

BobDog

  • Sr. Member
  • ****
  • Posts: 364
You can simulate a fall through, if you really must.
Code: Pascal  [Select][+][-]
  1.  
  2. {$macro on}
  3. {$define nobreak := end; case x of}
  4.    
  5. var
  6. x:integer=6;
  7.  
  8.  
  9. begin
  10. writeln('Number ',x);
  11.  
  12. case x of
  13.  
  14. 6:
  15.     writeln( 6);
  16.     nobreak
  17.    
  18.    
  19.  2 .. 7:
  20.     writeln( 2,' to ',7);
  21.     nobreak
  22.    
  23.  
  24. 9 .. 14:
  25.     writeln( 9,' to ',14);
  26.     nobreak
  27.    
  28.    
  29. 5 .. 7:
  30.     writeln( 5,' to ',7);
  31.     nobreak
  32.    
  33.    
  34. 12:
  35.     writeln( 12);
  36.  
  37. end;
  38. writeln('Press return to exit . . .');
  39. readln;
  40.  
  41. end.
  42.  
  43.  
  44.  
  45.  
  46.  
« Last Edit: August 16, 2022, 12:34:00 pm by BobDog »

y.ivanov

  • Hero Member
  • *****
  • Posts: 554
@SymbolicFrank, @BobDog
Since perhaps no one bothered to look at the protothreads, I can show a simple example. Consider the slightly modified previous one (from reply #35) with some parametrized macros added:
Code: C  [Select][+][-]
  1. #include <stdio.h>
  2.  
  3. #define C_BEGIN(q)      switch( q ) { case 0:
  4. #define C_END(q)        } q = 0; return -1;
  5. #define C_YIELD(q, r) do { q = __LINE__; return (r); case __LINE__: ; } while( 0 )
  6.  
  7. int function()
  8. {
  9.     static int i, state = 0;
  10.  
  11.     C_BEGIN( state );  
  12.     while( 1 )
  13.         for( i = 0; i < 10; i++ )
  14.             C_YIELD( state, i);
  15.     C_END( state );
  16. }
  17.  
  18. int main()
  19. {
  20.     for( int i = 0; i < 15; i++ )
  21.         printf("%d ", function()); // prints: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
  22. }

You can test it here: https://godbolt.org/z/r8jjnhnc9

Using C_BEGIN/C_END, and when you put C_YIELD in between, the function will exit and on next invocation will continue right after the C_YIELD. All local variables will lose their values, of course, but that is the price for the stackless switching.
 
Further, consider the skeleton:
Code: C  [Select][+][-]
  1. #include <stdio.h>
  2.  
  3. #define C_BEGIN(q)      switch( q ) { case 0:
  4. #define C_END(q)        } q = 0; return -1;
  5. #define C_YIELD(q, r)   do { q = __LINE__; return (r); case __LINE__: ; } while( 0 )
  6.  
  7. int comm_fn()
  8. {
  9.     static int q = 0;
  10.  
  11.     C_BEGIN( q );
  12.     while(1)
  13.     {
  14.         while( tx_buffer_empty() )
  15.             C_YIELD(q, 0);
  16.  
  17.         send_buffer();
  18.  
  19.         while( rx_buffer_empty() && !rx_timeout() )
  20.             C_YIELD(q, 0);
  21.  
  22.         if( !rx_buffer_empty() )
  23.         {
  24.             // process the rx data
  25.         }
  26.     }
  27.     C_END( q );
  28. }
  29.  
  30. int ui_fn()
  31. {
  32.     static int q = 0;
  33.  
  34.     C_BEGIN( q );
  35.     while(1)
  36.     {
  37.         printf( "enter command ('q' for quit)\n" );
  38.  
  39.         while( !user_input() )
  40.             C_YIELD(q, 0);
  41.  
  42.         if( last_input() == 'q' )
  43.             exit(0);
  44.  
  45.         else
  46.             ; // process other choices
  47.    }
  48.     C_END( q );
  49. }
  50.  
  51. int main()
  52. {
  53.     for( ;; )
  54.     {
  55.         comm_fn();
  56.         ui_fn();
  57.         // other coroutines ...
  58.     }
  59. }

In which two unrelated functions are executed together simultaneously much like coroutines.

And it is not only about fall-through, it is also about goto-like switch/case behavior and parametrized macros.

This is something not achievable with the Pascal case statement. It's just different.

*snip*

At least nobody introduced Turing equivalence...

MarkMLl

 
I was tempted to do so...
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1131
This is something not achievable with the Pascal case statement. It's just different.

Which is a good thing! Broken code shouldn't compile.

BobDog

  • Sr. Member
  • ****
  • Posts: 364

I just simulated fall through.
It would save many if/then's maybe.
macros with parameters would be nice.
So would proper variadic functions like c, and not just variadic from exported functions.
But pascal can use an array directly as a parameter, which is powerful.
dothis([2,6,7,2])
So strike that So would proper variadic functions like c
 Ternary operators (properly, passing over what could be an error or violation) are quite handy.
And the dreaded a+=1, which I know will upset many, should return probably.
All this is IMHO of course, I don't use pascal that much.
C has only 32 keywords, but it remains the most powerful high level language around, and like pascal is ancient.
Reminds me of:
"Old fiddles play the best tunes"








MarkMLl

  • Hero Member
  • *****
  • Posts: 5563
macros with parameters would be nice.

I agree, although it would need a great deal of thought to do properly.

Quote
So would proper variadic functions like c, and not just variadic from exported functions.
But pascal can use an array directly as a parameter, which is powerful.
dothis([2,6,7,2])
So strike that So would proper variadic functions like c

TBH, I'd not object to seeing WriteLn() etc. insist on the variable number of parameters being bracketed, i.e. in the same way that Format() does.

Quote
Ternary operators (properly, passing over what could be an error or violation) are quite handy.

I'd like to see the concise if-the-else expression that was in ALGOL reintroduced. But apart from that I'm wary of other ternaries (or above).

Quote
And the dreaded a+=1, which I know will upset many, should return probably.

Sven has patiently explained to me why even in the context of C that's a complex issue. Frankly I'd not be that bothered with the subtleties provided that they were properly documented, and would be entirely happy to see this being a predefined macro with no specific compiler support.

At which point I'd like to add two things. The first is that parameterised macros don't necessarily have to look like functions: in principle they could also look like Smalltalk keyword expressions.

The second is that IMO both C and Pascal are excessively find of automatic type conversions. I'd be happier with this not being the case, with conversion only being permitted by a trait or by an explicit overload of the assignment operator... which isn't going to happen in FPC since AIUI the type of the lvalue isn't taken into account when an overloaded function is selected.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

PascalDragon

  • Hero Member
  • *****
  • Posts: 4744
  • Compiler Developer
And the dreaded a+=1, which I know will upset many, should return probably.

a += 1 is a statement. Statements do not have a result value. This is a basic concept of Pascal and absolutely not up for discussion.

Quote
Ternary operators (properly, passing over what could be an error or violation) are quite handy.

I'd like to see the concise if-the-else expression that was in ALGOL reintroduced. But apart from that I'm wary of other ternaries (or above).

Which is the only one I personally would support as well... (and I had implemented it already)

The second is that IMO both C and Pascal are excessively find of automatic type conversions. I'd be happier with this not being the case, with conversion only being permitted by a trait or by an explicit overload of the assignment operator... which isn't going to happen in FPC since AIUI the type of the lvalue isn't taken into account when an overloaded function is selected.

For the assignment operator (and maybe also other operator overloads returning a custom type) the compiler does take the left side into account, after all it wouldn't be able to pick the correct one otherwise:

Code: Pascal  [Select][+][-]
  1. program topovld;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$modeswitch advancedrecords}
  5.  
  6. type
  7.   TTest = record
  8.     f: LongInt;
  9.     class operator := (const aArg: TTest): LongInt;
  10.     class operator := (const aArg: TTest): Boolean;
  11.   end;
  12.  
  13. class operator TTest.:= (const aArg: TTest): LongInt;
  14. begin
  15.   Result := aArg.f;
  16. end;
  17.  
  18. class operator TTest.:= (const aArg: TTest): Boolean;
  19. begin
  20.   Result := aArg.f <> 0;
  21. end;
  22.  
  23. var
  24.   t: TTest;
  25.   l: LongInt;
  26.   b: Boolean;
  27. begin
  28.   t.f := 42;
  29.   l := t;
  30.   b := t;
  31.  
  32.   Writeln(l, ' ', b); // prints "42 TRUE"
  33. end.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5563
For the assignment operator (and maybe also other operator overloads returning a custom type) the compiler does take the left side into account, after all it wouldn't be able to pick the correct one otherwise:

Thanks for that. Are you saying that, in effect, they have started taking more notice of the left side type comparatively recently?

I've always found that overloaded assignments needed to be approached with extreme caution, but perhaps it's time to have another play.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018