96

At the very end of Scott Schurr's talk "Introducing constexpr" at CppCon, he asks "Is there a way to poison a function"? He then explains that this can be done (albeit in a non-standard way) by:

  1. Putting a throw in a constexpr function
  2. Declaring an unresolved extern const char*
  3. Referencing the unresolved extern in the throw

I sense that I'm a bit out of my depth here, but I'm curious:

  • What does it mean to "poison a function"?
  • What is the significance/usefulness of the technique he outlines?
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sudo make install
  • 5,629
  • 3
  • 36
  • 48
  • 1
    Never heard about that term, clarify with a concise example please! – πάντα ῥεῖ Nov 27 '15 at 20:52
  • 6
    @πάνταῥεῖ, I just clarified. This a term 'widely known in small circles' – SergeyA Nov 27 '15 at 20:53
  • 4
    He's talking about ensuring that every call to the `constexpr` function is evaluated at compile time. – T.C. Nov 27 '15 at 20:55
  • @T.C. Right--he mentioned that a `constexpr` function could be used either at compile time or at run time. So this is a way to force it so that you can't use it at run time? When is that useful? – sudo make install Nov 27 '15 at 20:58
  • 3
    Especially in C++11, a `constexpr` function often isn't the most efficient implementation because of the constraints, so one may not want it evaluated at run time; or, maybe it's the error case (as in his example). – T.C. Nov 27 '15 at 21:00
  • @T.C. That makes a lot of sense--thanks! – sudo make install Nov 27 '15 at 21:01

2 Answers2

106

In general it refers to making a function unusable, e.g. if you want to ban the use of dynamic allocation in a program you could "poison" the malloc function so it can't be used.

In the video he's using it in a more specific way, which is clear if you read the slide that is displayed when he talks about poisoning the function, which says "A way to force compile-time only?"

So he is talking about "poisoning" the function to make it uncallable at run-time, so it's only callable in constant expressions. The technique is to have a branch in the function which is never taken when called in a compile-time context, and to make that branch contain something that will cause an error.

A throw expression is allowed in a constexpr function, as long as it is never reached during compile-time invocations of the function (because you can't throw an exception at compile-time, it's an inherently dynamic operation, like allocating memory). So a throw expression that refers to an undefined symbol will not be used during compile-time invocations (because that would fail to compile) and cannot be used at run-time, because the undefined symbol causes a linker error.

Because the undefined symbol is not "odr-used" in the compile-time invocations of the function, in practice the compiler will not create a reference to the symbol, so it's OK that it's undefined.

Is that useful? He's demonstrating how to do it, not necessarily saying it's a good idea or widely useful. If you have a need to do it for some reason then his technique might solve your problem. If you don't have a need for it, you don't need to worry about it.

One reason it might be useful is when the compile-time version of some operation is not as efficient as it could be. There are restrictions on the kind of expressions allowed in a constexpr function (especially in C++11, some restrictions were removed in C++14). So you might have two versions of a function for performing a calculation, one that is optimal, but uses expressions that aren't allowed in a constexpr function, and one that is a valid constexpr function, but would perform poorly if called at run-time. You could poison the sub-optimal one to ensure it is never used for run-time calls, ensuring the more efficient (non-constexpr) version is used for run-time calls.

N.B. The performance of a constexpr function used at compile-time is not really important, because it has no run-time overhead anyway. It might slow down your compilation by making the compiler do extra work, but it won't have any run-time performance cost.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    I did read the text of the slide, but I didn't see the connection to the term he was using. It's obvious now that you've explained it, but I didn't see it at the time. Thanks very much for this excellent answer--I just love this website. – sudo make install Nov 27 '15 at 21:13
  • @PravasiMeet, ask your own question, don't hijack the comments of someone else's question about something different. A simple solution would be to define it as deleted in every translation unit, or replace it with your own definition that references an undefined symbol. – Jonathan Wakely Dec 04 '15 at 16:42
19

'Poisoning' an identifier means that any reference to the identifier after the 'poisoning' is a hard compiler error. This technique may be used, for instance, for hard deprecation (function IS deprecated, never use it!).

In GCC traditionally there was a pragma for this: #pragma GCC poison.

SergeyA
  • 61,605
  • 5
  • 78
  • 137