Recent

Author Topic: Is Meta programming possible with fpc?  (Read 6652 times)

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1242
Is Meta programming possible with fpc?
« on: May 21, 2023, 06:20:29 pm »
Thanks for quick response
Is there any meta programming in pascal. Like the template mechanism in C++
https://en.wikipedia.org/wiki/Template_metaprogramming
« Last Edit: May 21, 2023, 06:47:53 pm by Joanna »
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: Is Meta programming possible with fpc?
« Reply #1 on: May 21, 2023, 06:37:10 pm »
I think not really because pascal lacks the ability to so to speak overload generics with specializations.

For example, take the example from Wikipedia:
Code: Pascal  [Select][+][-]
  1. template <unsigned N>
  2. struct factorial {
  3.         static constexpr unsigned value = N * factorial<N - 1>::value;
  4. };
  5.  
  6. template <>
  7. struct factorial<0> {
  8.         static constexpr unsigned value = 1;
  9. };
This defines the function factorial with a generic constant parameter. So far so easy, can be done in pascal (at least with trunk). But then it also defines the base case for when 0 is the parameter.
This basically says that: if the generic parameter is 0 use this function, otherwise use the other one for a generic N.

This is as far as I know not possible with fpc, that you can have special implementations for certain generic values. As this is the requirement for the recursive definition (as any recursive chain requires a base case to stop), you can't replicate such recursive generic functions, which are essential to archive the power of C++ template programming

Note that together with implicit specialization, if fpc were to support that, this would allow for very useful options, e.g. the ability to create generic functions that could generically free different types of data transparently:
Code: Pascal  [Select][+][-]
  1. procedure MyFinalize<T: TObject>(obj: T); // calls destructor of classes
  2. Begin
  3.   Obj.Free;
  4. End;
  5.  
  6. Procedure MyFinalize<T: object>(var obj: T); //calls destructor of TP style destructors
  7. Begin
  8.   obj.done;
  9. End;
  10.  
  11. Procedure MyFinalize<T>(var obj: T); //simply clears all other variables (strings, arrays, records)
  12. Begin
  13.   obj := Default(T);
  14. End;
This would enable to just call MyFinalize(obj) independently if if obj  is a class instance or if it is an old TP style object or anything else. So to say one free for any data type, no need to think what kind of data you have there( would need another case for pointers but I am lazy in this example, and typing on my phone is annoying)
This is especially useful for containers that should be able to hold any kind of data.
« Last Edit: May 21, 2023, 06:50:45 pm by Warfley »

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1242
Re: Is Meta programming possible with fpc?
« Reply #2 on: May 21, 2023, 06:47:25 pm »
Are there any downsides to this type of meta programming or workarounds for something similar in pascal?

Also is there a reason to use generics instead of a base class and descendants ?
« Last Edit: May 21, 2023, 06:50:52 pm by Joanna »
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: Is Meta programming possible with fpc?
« Reply #3 on: May 21, 2023, 06:53:27 pm »
The downside is that you can produce massively complicated code. C++ SFINAE completely made my head spin when I first encountered it. Extremely powerful, but if you are new to that sort of stuff it can be really hard to understand

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1242
Re: Is Meta programming possible with fpc?
« Reply #4 on: May 21, 2023, 07:01:29 pm »
Is it hard to debug also? Is there a reason pascal doesn’t have it?
Someone told me recently about the  c++ “diamond of death” Recently
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: Is Meta programming possible with fpc?
« Reply #5 on: May 21, 2023, 07:18:46 pm »
Well there is no "debugging" as it is completely compiletime. You run your compiler and if you made a mistake you get a few hundred lines of compiler messages that goes through all the substitutions and you need to spot where it went wrong.

When I have worked with C++, templating errors where by far the hardest to decipher from the compiler output (but maybe it has gotten better, haven't used C++ since 2019)

Leledumbo

  • Hero Member
  • *****
  • Posts: 8777
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Is Meta programming possible with fpc?
« Reply #6 on: May 21, 2023, 08:27:30 pm »
How would meta programming be substituted for in pascal to achieve the same results as far as program behavior ? Is it possible?
There is none for now. That factorial example in the wikipedia article, for example, is currently not possible. Generics is as far as FPC has currently.

I don't think anyone sane would want that kind of error message that @Warfley said. Yes, even in 2023 the error message is still hella cryptic. Here's a wrong attempt trying to implement a custom struct as a key for map:
Code: C++  [Select][+][-]
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. struct coord {
  5.     int x, y;
  6.  
  7.     bool operator=(const coord &o) {
  8.         return x == o.x && y == o.y;
  9.     }
  10.  
  11.     bool operator<(const coord &o) {
  12.         return x < o.x || (x == o.x && y < o.y);
  13.     }
  14. };
  15.  
  16. int main() {
  17.     map<coord, int> m;
  18.     pair<coord, int> p((coord{0,0}),123);
  19.     m.insert(p);
  20.     return 0;
  21. }
  22.  
the offending line is:
Code: C++  [Select][+][-]
  1. bool operator<(const coord &o) {
  2.  
which should be (i.e. it forgot to mark the operator definition as const):
Code: C++  [Select][+][-]
  1. bool operator<(const coord &o) const {
  2.  
now the error message it spits:
Code: [Select]
$ g++ -Wall -o test test.cpp
In file included from /usr/include/c++/12.2.1/string:48,
                 from /usr/include/c++/12.2.1/bits/locale_classes.h:40,
                 from /usr/include/c++/12.2.1/bits/ios_base.h:41,
                 from /usr/include/c++/12.2.1/ios:42,
                 from /usr/include/c++/12.2.1/istream:38,
                 from /usr/include/c++/12.2.1/sstream:38,
                 from /usr/include/c++/12.2.1/complex:45,
                 from /usr/include/c++/12.2.1/ccomplex:39,
                 from /usr/include/c++/12.2.1/x86_64-pc-linux-gnu/bits/stdc++.h:54,
                 from test.cpp:1:
/usr/include/c++/12.2.1/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = coord]’:
/usr/include/c++/12.2.1/bits/stl_map.h:856:35:   required from ‘std::__enable_if_t<std::is_constructible<std::pair<const _Key, _Val>, _Pair>::value, std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Val>, std::_Select1st<std::pair<const _Key, _Val> >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other>::iterator, bool> > std::map<_Key, _Tp, _Compare, _Alloc>::insert(_Pair&&) [with _Pair = std::pair<coord, int>&; _Key = coord; _Tp = int; _Compare = std::less<coord>; _Alloc = std::allocator<std::pair<const coord, int> >; std::__enable_if_t<std::is_constructible<std::pair<const _Key, _Val>, _Pair>::value, std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Val>, std::_Select1st<std::pair<const _Key, _Val> >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other>::iterator, bool> > = std::pair<std::_Rb_tree_iterator<std::pair<const coord, int> >, bool>; typename std::_Rb_tree<_Key, std::pair<const _Key, _Val>, std::_Select1st<std::pair<const _Key, _Val> >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other>::iterator = std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int> >, std::less<coord>, std::allocator<std::pair<const coord, int> > >::iterator; typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other = std::allocator<std::pair<const coord, int> >; typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> > = __gnu_cxx::__alloc_traits<std::allocator<std::pair<const coord, int> >, std::pair<const coord, int> >::rebind<std::pair<const coord, int> >; typename _Allocator::value_type = std::pair<const coord, int>]’
test.cpp:19:13:   required from here
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: error: no match for ‘operator<’ (operand types are ‘const coord’ and ‘const coord’)
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
test.cpp:11:10: note: candidate: ‘bool coord::operator<(const coord&)’ (near match)
   11 |     bool operator<(const coord &o) {
      |          ^~~~~~~~
test.cpp:11:10: note:   passing ‘const coord*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c++/12.2.1/bits/stl_algobase.h:64,
                 from /usr/include/c++/12.2.1/bits/specfun.h:45,
                 from /usr/include/c++/12.2.1/cmath:1935,
                 from /usr/include/c++/12.2.1/x86_64-pc-linux-gnu/bits/stdc++.h:41:
/usr/include/c++/12.2.1/bits/stl_pair.h:663:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator<(const pair<_T1, _T2>&, const pair<_T1, _T2>&)’
  663 |     operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
      |     ^~~~~~~~
/usr/include/c++/12.2.1/bits/stl_pair.h:663:5: note:   template argument deduction/substitution failed:
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: note:   ‘const coord’ is not derived from ‘const std::pair<_T1, _T2>’
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
In file included from /usr/include/c++/12.2.1/bits/stl_algobase.h:67:
/usr/include/c++/12.2.1/bits/stl_iterator.h:451:5: note: candidate: ‘template<class _Iterator> constexpr bool std::operator<(const reverse_iterator<_Iterator>&, const reverse_iterator<_Iterator>&)’
  451 |     operator<(const reverse_iterator<_Iterator>& __x,
      |     ^~~~~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:451:5: note:   template argument deduction/substitution failed:
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: note:   ‘const coord’ is not derived from ‘const std::reverse_iterator<_Iterator>’
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:496:5: note: candidate: ‘template<class _IteratorL, class _IteratorR> constexpr bool std::operator<(const reverse_iterator<_Iterator>&, const reverse_iterator<_IteratorR>&)’
  496 |     operator<(const reverse_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:496:5: note:   template argument deduction/substitution failed:
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: note:   ‘const coord’ is not derived from ‘const std::reverse_iterator<_Iterator>’
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:1683:5: note: candidate: ‘template<class _IteratorL, class _IteratorR> constexpr bool std::operator<(const move_iterator<_IteratorL>&, const move_iterator<_IteratorR>&)’
 1683 |     operator<(const move_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:1683:5: note:   template argument deduction/substitution failed:
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: note:   ‘const coord’ is not derived from ‘const std::move_iterator<_IteratorL>’
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:1748:5: note: candidate: ‘template<class _Iterator> constexpr bool std::operator<(const move_iterator<_IteratorL>&, const move_iterator<_IteratorL>&)’
 1748 |     operator<(const move_iterator<_Iterator>& __x,
      |     ^~~~~~~~
/usr/include/c++/12.2.1/bits/stl_iterator.h:1748:5: note:   template argument deduction/substitution failed:
/usr/include/c++/12.2.1/bits/stl_function.h:408:20: note:   ‘const coord’ is not derived from ‘const std::move_iterator<_IteratorL>’
  408 |       { return __x < __y; }
      |                ~~~~^~~~~
Code: [Select]
$ clang++ -Wall -o test test.cpp
In file included from test.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/x86_64-pc-linux-gnu/bits/stdc++.h:54:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/ccomplex:39:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/complex:45:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/sstream:38:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/istream:38:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/ios:42:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/ios_base.h:41:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/locale_classes.h:40:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/string:48:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_function.h:408:20: error: invalid operands to binary expression ('const coord' and 'const coord')
      { return __x < __y; }
               ~~~ ^ ~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_tree.h:2117:13: note: in instantiation of member function 'std::less<coord>::operator()' requested here
          __comp = _M_impl._M_key_compare(__k, _S_key(__x));
                   ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_tree.h:2434:15: note: in instantiation of member function 'std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int>>, std::less<coord>>::_M_get_insert_unique_pos' requested here
        auto __res = _M_get_insert_unique_pos(__z._M_key());
                     ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_map.h:864:16: note: in instantiation of function template specialization 'std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int>>, std::less<coord>>::_M_emplace_unique<std::pair<coord, int> &>' requested here
          return _M_t._M_emplace_unique(std::forward<_Pair>(__x));
                      ^
test.cpp:19:7: note: in instantiation of function template specialization 'std::map<coord, int>::insert<std::pair<coord, int> &>' requested here
    m.insert(p);
      ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_iterator.h:451:5: note: candidate template ignored: could not match 'const reverse_iterator<_Iterator>' against 'const coord'
    operator<(const reverse_iterator<_Iterator>& __x,
    ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_iterator.h:496:5: note: candidate template ignored: could not match 'const reverse_iterator<_IteratorL>' against 'const coord'
    operator<(const reverse_iterator<_IteratorL>& __x,
    ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_iterator.h:1683:5: note: candidate template ignored: could not match 'const move_iterator<_IteratorL>' against 'const coord'
    operator<(const move_iterator<_IteratorL>& __x,
    ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_iterator.h:1748:5: note: candidate template ignored: could not match 'const move_iterator<_Iterator>' against 'const coord'
    operator<(const move_iterator<_Iterator>& __x,
    ^
test.cpp:11:10: note: candidate function not viable: 'this' argument has type 'const coord', but method is not marked const
    bool operator<(const coord &o) {
         ^
In file included from test.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/x86_64-pc-linux-gnu/bits/stdc++.h:81:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/map:60:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_tree.h:762:2: error: static assertion failed due to requirement 'std::__is_invocable<std::less<coord> &, const coord &, const coord &>{}': comparison object must be invocable with two arguments of key type
        static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
        ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_tree.h:2117:41: note: in instantiation of member function 'std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int>>, std::less<coord>>::_S_key' requested here
          __comp = _M_impl._M_key_compare(__k, _S_key(__x));
                                               ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_tree.h:2434:15: note: in instantiation of member function 'std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int>>, std::less<coord>>::_M_get_insert_unique_pos' requested here
        auto __res = _M_get_insert_unique_pos(__z._M_key());
                     ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/stl_map.h:864:16: note: in instantiation of function template specialization 'std::_Rb_tree<coord, std::pair<const coord, int>, std::_Select1st<std::pair<const coord, int>>, std::less<coord>>::_M_emplace_unique<std::pair<coord, int> &>' requested here
          return _M_t._M_emplace_unique(std::forward<_Pair>(__x));
                      ^
test.cpp:19:7: note: in instantiation of function template specialization 'std::map<coord, int>::insert<std::pair<coord, int> &>' requested here
    m.insert(p);
      ^
2 errors generated.
Here's a similar attempt for FPC with fgl (cannot reproduce with generics.collections as it manages to avoid the need for operator overloading):
Code: Pascal  [Select][+][-]
  1. uses fgl;
  2.  
  3. type
  4.   tr = record end;
  5.   tm = specialize tfpgmap<tr,integer>;
  6. var
  7.   m: tm;
  8. begin
  9. end.
  10.  
Code: [Select]
$ fpc test.pas
Target OS: Linux for x86-64
Compiling test.pas
fgl.pp(1591,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1601,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1596,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1605,18) Error: Operator is not overloaded: "tr" < "tr"
fgl.pp(1607,23) Error: Operator is not overloaded: "tr" > "tr"
fgl.pp(1660,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1670,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1665,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1680,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1675,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1685,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1697,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1702,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1707,1) Note: "inherited" not yet supported inside inline procedure/function
test.pas(11) Fatal: There were 2 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: Is Meta programming possible with fpc?
« Reply #7 on: May 21, 2023, 08:50:06 pm »
Well I know of a workaround that I personally use, I have code generation scripts I run as a pre build hook. Not pretty but it works

Bart

  • Hero Member
  • *****
  • Posts: 5469
    • Bart en Mariska's Webstek
Re: Is Meta programming possible with fpc?
« Reply #8 on: May 21, 2023, 10:36:59 pm »
Here's a similar attempt for FPC with fgl (cannot reproduce with generics.collections as it manages to avoid the need for operator overloading):
Code: Pascal  [Select][+][-]
  1. uses fgl;
  2.  
  3. type
  4.   tr = record end;
  5.   tm = specialize tfpgmap<tr,integer>;
  6. var
  7.   m: tm;
  8. begin
  9. end.
  10.  
Code: [Select]
$ fpc test.pas
Target OS: Linux for x86-64
Compiling test.pas
fgl.pp(1591,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1601,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1596,1) Note: "inherited" not yet supported inside inline procedure/function
fgl.pp(1605,18) Error: Operator is not overloaded: "tr" < "tr"
fgl.pp(1607,23) Error: Operator is not overloaded: "tr" > "tr"

IIRC then you need to specify these operators as part of the record definition.

Bart

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1242
Re: Is Meta programming possible with fpc?
« Reply #9 on: May 22, 2023, 02:32:36 am »
Thanks for all the great responses.As some of you might have guessed my question was inspired by a recently conversation with someone whom I was trying to get interested in Lazarus.

I would think that if meta programming was so indispensable it would have been created for fpc. I’ve never felt a need to do this sort of stuff because my programming is not that advanced.

Quote
Well I know of a workaround that I personally use, I have code generation scripts I run as a pre build hook. Not pretty but it works
I’m glad there is a way to get desired results.
I don’t know if trying to imitate every Interesting feature that other languages offer in fpc project is a good idea or not. It could certainly lead to bloat.

I’ve just been trying to come up with good answers to questions along the lines of ..can fpc do such and such like this other language...

✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

440bx

  • Hero Member
  • *****
  • Posts: 4757
Re: Is Meta programming possible with fpc?
« Reply #10 on: May 22, 2023, 03:55:45 am »
I'd like to offer another viewpoint about metaprogramming.

First, I don't consider it advanced in any way.  Specifically, anything that for the most part gratuitously complicates matters, isn't advanced, it's deficient/obtuse/"ill conceived".  In the accomplishment of those characteristics, it might be an advanced technique (but that doesn't make it desirable.)

I subscribe to the rule that features that allow creating simpler, easier to understand code are good features and, those that do not or, worse make the code harder to understand, are bad/undesirable features and, so far, metaprogramming falls squarely in the latter.

Templates are inherently compiler-expanded macros (instead of preprocessor-expanded macros) and, they both share one undesirable characteristic which is: what the _programmer_ reads isn't the source code that the compiler will compile, instead it is some text to which some transformation will be applied to generate source code that the compiler will process.  The net effect is, the _programmer_ is _saddled_ to mentally apply the transformation in order to see the code the compiler will process.    IOW, more work for the human mind (usually in the interest of saving a very small amount of work for the human fingers... what a deal !!.)

There is one good thing I can say about metaprogramming, I hope it keeps the programmers who love it busy enough that they don't have time to write another ill-conceived compiler for two finger typists.


(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

alpine

  • Hero Member
  • *****
  • Posts: 1303
Re: Is Meta programming possible with fpc?
« Reply #11 on: May 22, 2023, 09:03:48 am »
Metaprogramming in C++ is based on:
template explicit and partial specialization, default template arguments, template non-type arguments, template template arguments.
Some of features are good to have in FPC generics implementation, thought.

Quote
I’ve just been trying to come up with good answers to questions along the lines of ..can fpc do such and such like this other language...
Personal opinion: metaprogramming isn't worth arguing about.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Warfley

  • Hero Member
  • *****
  • Posts: 1771
Re: Is Meta programming possible with fpc?
« Reply #12 on: May 22, 2023, 11:44:02 am »
I’m glad there is a way to get desired results.
I don’t know if trying to imitate every Interesting feature that other languages offer in fpc project is a good idea or not. It could certainly lead to bloat.

I’ve just been trying to come up with good answers to questions along the lines of ..can fpc do such and such like this other language...

Metaprogramming in a more general term (not just the C++ style Template Metaprogramming) basically allows you to extend the language without having to touch the formal definition of the grammar or the compiler. This can be really useful. For example Pascal has a lot of compiler magic. One of the most magical functions is WriteLn, it can take an arbitrary number of Parameters and print them. More importantly, unlike "array of const" or other variant based structures, this is compile time decided not runtime, and it works with all kinds of types like enums:
Code: Pascal  [Select][+][-]
  1. var
  2.   i: Integer = 42;
  3.   e: TMyEnum = meSomeValue;
  4.   d: Double = 3.14;
  5.   c: Char = 'A';
  6.   s: String 'Hello World';
  7. begin
  8.   WriteLn(i, e, d, c, s); // Prints all those variables
This is hardcoded in the compiler, there is no way on earth to build such a function yourself using pure Pascal. But in C++ this is easy:
Code: C  [Select][+][-]
  1. void WriteLn() {
  2.     std::cout << '\n';
  3. }
  4.  
  5. template <typename T, typename ...Args>
  6. void WriteLn(T const &first, Args... rest) {
  7.     std::cout << first;
  8.     WriteLn(rest...);
  9. }
  10.  
  11. int main() {
  12.     WriteLn('H', "ello World", 3.14, 42);
  13.  
  14.     return 0;
  15. }

So in C++ you can rebuilt the compilermagic from pascal in language through meta programming.

That can be really useful. For one example where I used a generator script is in my STAX, there I have tasks, which run asynchronously. These Tasks in the Scheduler are Classes, but because I want to be able to simply call functions asynchronously:
Code: Pascal  [Select][+][-]
  1.   s := Await(AsyncFunction(@SomeFunctionThatReturnsString, Parameters));
I somehow need to write the AsyncFunction function that turns a function pointer and a list of parameters into a Task object that internally calls the function with these parameters.
The problem is, because Pascal Generics are quite limited, I can only define one AsyncFunction definition for a specific number of arguments, so to support doing this with a function that takes one parameter requires a seperate implementation of the same taking two parameters, etc. Not to mention that there is a difference between function, method and nested function pointers, which basically tripples the effort.

So what I did was to simply write a generator that would generate all these definitions for me for functions with 0-5 parameters. The result can be seen here: https://github.com/Warfley/STAX/blob/master/src/functional/stax.functional.pas

Using a code generator is, by the more liberal definition of "meta programming" in general a form of it. But of course it's not really practical, and if someone needs lets sa a function with 6 parameters, the generator script needs to be changed. This would all not be necessary if there was some in-language way to do this metaprogramming

Metaprogramming has it's place, for example it is undeniable that the metaprogramming that LISP provides through it's inherent design (Data is Code) and the LISP Macros, is what has kept LISP not just alive, but at it's core mostly unchanged for the past 70 years. There is this joke that LISP is actually the perfect language, because while being one of the oldest programmin languages in existance, it is still quite popular without any large changes to it's design over it's whole lifetime, and most changes that there are were made in language through metaprogramming. As comparison, even C has been under heavy change between ANSI C, C89, C99 and C11. C++ has had major updates in 2011 and 2020, and Pascal has also gone through many iterations until where we are now., because it was one of the first major programming languages, and is the only one thats still around without going throuhg major changes.

Metaprogramming can make a language future proof without having to constantly update the language and create new features. But on the other hand, if you look how it went for C++, advanced C++ with template programming is borderline undecipherable for anyone who does not have some background in C++, with new things being added regularly making it often more complex, as new features are introduced constantly.
So personally, I don't think that C++ is necessarily a model to folow
« Last Edit: May 22, 2023, 11:48:06 am by Warfley »

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1242
Re: Is Meta programming possible with fpc?
« Reply #13 on: May 22, 2023, 02:27:14 pm »
That is interesting how writeln can different parameters Sent to it on different occasions and still work. I wonder where the code that does that is located, I’d like to see it.

I Also agree with the concept that things shouldn’t be over complicated just for the sake of being fancy, if the same can be accomplished with easier to maintain code.
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

mercurhyo

  • Sr. Member
  • ****
  • Posts: 253
Re: Is Meta programming possible with fpc?
« Reply #14 on: May 22, 2023, 04:17:05 pm »
hmhm I wonder who told you about "diamond of death"  :D

about "writeln" accepting many parameters you can do it yourself with any procedure/function you wrrite

 :P

https://www.freepascal.org/docs-html/ref/refsu69.html
DEO MERCHVRIO - Linux, Win10pro - Ryzen9XT 24threads + Geforce Rtx 3080SUPRIM
god of financial gain, commerce, eloquence (and thus poetry), messages, communication (including divination), travelers, boundaries, luck, trickery and thieves; he also serves as the guide of souls to the underworld

 

TinyPortal © 2005-2018