On Friday, October 21, 2011 12:04 PM, Richard Guenther wrote:
> What's the motivation for this?  Why can't it be implemented using C++
> features such as templates and specialization?

Sorry for the delay in my reply.

The motivation, simply put, is to be able to create both templates and 
macros that choose between two or more courses of action at compile time and 
do not generate code for the unchosen action(s), nor (importantly) cause 
compiler errors if the unchosen action(s) should be ill-formed.  The end aim 
is in the generation of source code libraries for compile-time evaluation.

An example in the template domain would be, having template specialisation 
without actually needing to specialise the template.  Ordinarily, 
specialising a template requires a copy to be made and modified according to 
the specialisation.  If the "base" template is changed, the specialisation 
also needs to be so adapted.  Missing one specialisation in a long list 
leads to unpredictable problems (yes, there is bitter experience here!).  If 
the majority of code is identical then it makes sense to try to either 
factor out the identical code and use some meta-template If struct or other 
such magic to limit the scope of the template specialisation.  This can 
become brain-bending if not impossible if, for example, any of the relevant 
expressions cannot be parameters into a meta-template construct.  It can 
also make the code very difficult to follow and debug.  In these cases, 
__builtin_choose_expr can be beneficial to avoid the need to specialise, 
while keeping the clarity and breadth of the template's intended function.

When it comes to macros, the use of __builtin_choose_expr can be very useful 
where one of the expressions would cause a compiler error.  An example of 
such a macro is as follows:

#define CONST(v) \
   __builtin_choose_expr(util::can_use_const_wrapper<decltype(v)>::value, \
       (util::const_wrapper<decltype(v),(v)>::value) : \
       ([&](){ constexpr auto o = (v); return o; })())

namespace util
 {
 template <typename T, T t>
 struct const_wrapper
  { static constexpr T value = t; };

 template <typename T>
 struct can_use_const_wrapper; /* code removed to keep example short! */
 /* value = true for types that can be used in template value parameters */
 }

This is a part of a larger library of code for constexpr functions, a 
majority of which implement algorithms in such a way that the compiler can 
evaluate them but which if evaluated at run-time would have a huge 
performance hit (e.g. CRC calculation on a string).  In this library, a 
second macro FORCE_CONSTEXPR is used to ensure these functions cannot be 
called at run-time or even inlined, but only evaluated at compile-time.  The 
CONST macro is then used, among other things, to hint to the compiler that 
it needs to evaluate at compile-time.

If __builtin_choose_expr in the CONST macro was replaced with "? :" then the 
code using it would fail to compile in certain situations, notably because 
C++ only permits certain types as template value parameters.  However, it is 
not sufficient either to simply use the lambda expression alone since this 
fails with compiler errors in other situations.  This is, I think, one 
example where it is not possible to use templates to get round the 
problem?...

There is a further use of __builtin_choose_expr in this constexpr library, 
which is similar to its use in C, which is to enable a macro function to 
wrap and choose between a compile-time version of the algorithm and a 
run-time version of the algorithm, for example:

#define CrcString(str) (__builtin_choose_expr(__builtin_constant_p(str), \
                                       CONST(CrcString_compiler(str)),   \
                                       CrcString_runtime(str)))

auto string = CrcString("test");

This might of course be implemented using "? :", but only if 
CrcString_compiler(...) and CrcString_runtime(...) return compatible types, 
which need not be so with __builtin_choose_expr.

I hope this gives you some feeling for how __builtin_choose_expr may be 
used, or for that matter how I use it.  I expect that others will find much 
more impressive uses!

Thanks

Andy



Reply via email to