The following shows what the compiler would have to do.
I am not sure if it's /allowed/ to do such a rewrite.
(To make this compile without the referred library, if that's desired,
just #include the necessary standard library headers and replace
`$use_std` with `using`-declarations.)
----------------------------------------------------------------------
#include <cppx-core/all.hpp> // <url:
https://github.com/alf-p-steinbach/cppx-core>
namespace my{
$use_std( clog, endl, ostringstream, setw, stack, string );
class Logger
{
static inline int level = 0;
string m_indent;
string m_funcname;
string m_arglist;
public:
~Logger()
{
--level;
clog << m_indent << "< " << m_funcname << m_arglist << endl;
}
template< class... Args >
Logger( const string& funcname, const Args&... args ):
m_indent( 4*level, ' ' ),
m_funcname( funcname )
{
if constexpr( sizeof...( args ) > 0 ) {
ostringstream stream;
stream << "(";
auto output = [&stream, n = 0]( auto v ) mutable -> int
{
if( n > 0 ) { stream << ", "; }
stream << v;
++n;
return 0;
};
const int a[] = { output( args )... }; (void)a;
stream << ")";
m_arglist = stream.str();
}
clog << m_indent << "> " << m_funcname << m_arglist << endl;
++level;
}
};
auto recursive_gcd( const int a, const int b )
-> int
{
const Logger logging( __func__, a, b );
const int r = a % b;
if( r == 0 ) { return b; }
return recursive_gcd( b, r );
}
auto tail_optimized_gcd( int a, int b )
-> int
{
stack<Logger> logging_stack;
for( ;; )
{
logging_stack.emplace( __func__, a, b );
const int r = a % b; // const int r = a % b;
if( r == 0 ) // if( r == 0 ) {
return b; }
{
while( not logging_stack.empty() ) { logging_stack.pop(); }
return b;
}
a = b; b = r; // return
recursive_gcd( b, r );
}
}
} // namespace my
namespace app{
$use_std( cout, endl );
void run()
{
cout << "Recursive:" << endl;
const int r_gcd = my::recursive_gcd( 60, 16 );
cout << "recursive_gcd(60, 16) = " << r_gcd << endl;
cout << endl;
cout << "Tail-optimized:" << endl;
const int to_gcd = my::tail_optimized_gcd( 60, 16 );
cout << "tail_optimized_gcd(60, 16) = " << to_gcd << endl;
}
} // namespace app
auto main() -> int { app::run(); }
----------------------------------------------------------------------
Output:
Recursive:
> recursive_gcd(60, 16)
> recursive_gcd(16, 12)
> recursive_gcd(12, 4)
< recursive_gcd(12, 4)
< recursive_gcd(16, 12)
< recursive_gcd(60, 16)
recursive_gcd(60, 16) = 4
Tail-optimized:
> tail_optimized_gcd(60, 16)
> tail_optimized_gcd(16, 12)
> tail_optimized_gcd(12, 4)
< tail_optimized_gcd(12, 4)
< tail_optimized_gcd(16, 12)
< tail_optimized_gcd(60, 16)
tail_optimized_gcd(60, 16) = 4
Irrelevant to the question of tail optimization, but I was surprised
that one had to manually clear the stack to get LIFO destruction. Hm!
Cheers!,
- Alf