
How to abuse a C++ compiler? - AndreyKarpov
http://mysticalprogramming.wordpress.com/2014/03/19/how-to-abuse-a-cpp-compiler/
======
vinkelhake
It should be noted that at least some cases which previously required template
meta programming can now be done in a more natural style with constexpr. A
constexpr function can be evaluated at compile time, this means that its
return value can be used in places that require a compile time constant.

constexpr functions where quite restrictive in C++11. They could pretty much
only contain a single expression and a return. C++14 significantly relaxes
these restrictions.

[http://en.wikipedia.org/wiki/C%2B%2B14#Relaxed_constexpr_res...](http://en.wikipedia.org/wiki/C%2B%2B14#Relaxed_constexpr_restrictions)

Type-level meta programming is still confined to using templates though...

~~~
hun-nemethpeter
I tried to make a proposal for a more natural meta programming:

[https://github.com/hun-nemethpeter/cpp-reflector-
mini/blob/m...](https://github.com/hun-nemethpeter/cpp-reflector-
mini/blob/master/Proposal.md)

------
michaelfeathers
I think it is neck and neck whether C++ template instantiation or XSLT is the
ugliest unintentional functional programming language.

~~~
alexk7
I'm pretty sure XSLT functional paradigm was intentional.

~~~
1ris
I don't think it's even functional, as it lacks lambdas. It's just pure
insane.

~~~
Tobu
It's declarative at any rate. It's verbose but as long as it isn't used like
an imperative language, I don't find it ugly.

------
ohyes
It's funny that in C++ this is 'abuse of the compiler' and it is awkward, in
lisp this is called 'writing lisp code'.

~~~
marcosdumay
That's because it is awkward.

One of the fundamental properties people expect from a step called
"compilation" is that it finishes. Also templates notation wasn't designed for
this kind of use, and does not scale well for complex programs.

In Lisp code generation is done in Lisp, a notation that scales quite well,
and there isn't a separarted step called "compilation". There is no general
assumption that "execution" finishes.

~~~
cbsmith
> That's because it is awkward.

It's more than a bit awkward really.

Still, the OP is right: it's funny.

------
CraigJPerry

         ... in order to evaluate InfRec<T>::value ...
    

I've been spoiled lately, this is jarring to read.

    
    
        InfiniteRecursion<T>::value
    

C'mon, spoil me some more, text is cheap :-)

~~~
magnusjonsson
Horizontal space isn't.

------
cousin_it
I don't understand clang's behavior in the last three examples, but then again
I'm not a C++ expert. Is it conforming, and is it reasonable?

~~~
knome
It looks like clang creates it's data structure representing the template
instantiation, caches the instantiation for the parameterizing types, then
begins instantiating the types for the members. When it first hits the static
bool, it reserves space for it's static value, then recurses into the static
bools type in order to determine the value. Since the recursion uses the same
parameterizing types, it finds the type it was midway through constructing,
finds the space-reserved-but-no-appropriate-value-set static member in it,
takes a 0 from that memory ( presumably from having allocated and zeroed the
memory when initially saving space for the final value ).

For the MutualRec it may not set the static const value as being derived from
a constexpr until after having retrieved the value, causing it to access the
member in the cache before it's constexpr'ness has been determined.

For the Contra example, it would return 1 from having not'ed the 0 it found in
the uninitialized static const.

Russel's paradox would be subverted in the same manner as the Contra example,
returning true because it found false ( 0 ) in an uninitialized member
variable after looking up the partially instatiated template instantiation in
a cache.

This is all conjecture, but it fits, and it's where I would look first if
debugging this. I do not know whether this is allowed by the standard, but I'd
wager they did not account for referring cyclically to partially instantiated
template parameterizations in a cache. GCC's errorful death is probably the
correct action here, rather than clang's return of spurious values.

~~~
pbsd
You are correct. The Contra example is not different from the much simpler
program

    
    
        #include <iostream>
    
        int main()
        {
            static bool p = !p;
            std::cout << p << std::endl;
        }
    

The static storage specification ensures that p is initialized to 0 before
main is executed, and the standard's scoping rules (§3.3.2) allows a variable
to reference itself right after declaration.

The same thing happens with Russell's Paradox, with the added twist that
HasElement<M, M> always resolves to the specialization HasElement<M, T> (it
'fits' better) rather than the general version, which is never used.

~~~
knome

        ~/test/O$ cat break.cpp 
        
        #include <iostream>
        
        template <typename A, typename B>
        struct MutualRecursion {
          static const int a = MutualRecursion< B, A >::b ;
          static const int b = MutualRecursion< B, A >::a ;
        };
        
        int main( int argc, char ** argv ){
          std::cout << MutualRecursion< float, int >::b << std::endl;
          return 0;
        };
        ~/test/O$ clang break.cpp 
        break.cpp:6:49: error: no member named 'b' in 'MutualRecursion<float, int>'
          static const int a = MutualRecursion< B, A >::b ;
                               ~~~~~~~~~~~~~~~~~~~~~~~~~^
        break.cpp:6:24: note: in instantiation of template class 'MutualRecursion<int, float>' requested here
          static const int a = MutualRecursion< B, A >::b ;
                               ^
        break.cpp:11:16: note: in instantiation of template class 'MutualRecursion<float, int>' requested here
          std::cout << MutualRecursion< float, int >::b << std::endl;
                       ^
        break.cpp:7:24: error: in-class initializer is not a constant expression
          static const int b = MutualRecursion< B, A >::a ;
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
        2 errors generated.
        ~/test/O$ 
    

~~This is definitely a bug in clang~~. clang doesn't find the member "b" here
because it's referencing the cached still-in-construction version of the
template. Furthermore, it still does this even when "b" is a regular constant.

    
    
          // static const int b = MutualRecursion< B, A >::a ;                                                                                                                                                                                                                                                                                         
          static const int b = 10 ;
    

gcc, since it appears to always instantiate a new copy of the template
compiles successfully for when "b = 10".

~~clang needs to wait until it has fully constructed a type before putting it
into the cache, even if it might be slower in some recursive cases. Better
than wrong in them.~~

I don't know if clang is in the wrong here, so I'm rejecting a couple of
statements. Its method does not allow for compilation in these circumstances,
but may be technically permissible, even if gcc jives with how I envision
templates operating.

~~~
pbsd
I don't see how that could not be a compiler error. The case b = 10 is more
interesting because, as far as I can tell, it shows the differences between
compilers that do two-phase lookup correctly and those that don't.

This seems to be a rare case where GCC is in the right, but I'd have to
double-check the standard to be sure.

~~~
knome
It was pointed out to me after I joined #llvm when considering reporting this
that using the value of "b", which is defined after "a" in the class before it
has been initialized is, I suppose obviously in retrospect, undefined
behavior.

In effect, I was subverting the undefined behavior associated with expanding a
set of infinitely deep template instantiations by accessing a variable that
was as yet undefined at the point of access.

So, my initial retractions stand, and this compiler behavior will remain
appropriately undefined.

------
theophrastus
Fingermann's assertion: at the point where a preprocessor becomes Turing
complete it is time to write a preprocessor for it

~~~
unwind
C++'s template mechanism is not implemented by the preprocessor.

Templates control actual code generation (that's sort of their point, with
generics) so they must be handled later in the compilation, by the compiler
proper.

------
jasallen
as far as I can tell, the Russell's paradox example would be the same without
the template "template<typename U, typename V>"?

in other words, both compilers seem to be being consistent with the previous
demonstrated behavior using self-recursion, and the more generic template is
being ignored altogether.

Am I perhaps missing a reason it was included?

------
cordite
The first thing I thought was using C++ templates like Haskell.

~~~
cbsmith
Not that unusual, though there are some subtle differences in what C++
templates can do.

