// bsla_nodiscard.h -*-C++-*- #ifndef INCLUDED_BSLA_NODISCARD #define INCLUDED_BSLA_NODISCARD #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a macro for warning about ignored function results. // //@MACROS: // BSLA_NODISCARD: warn if annotated function result is not used // BSLA_NODISCARD_IS_ACTIVE: 1 if 'BSLA_NODISCARD' is active and 0 otherwise // BSLA_NODISCARD_CPP17: insert C++17 '[[nodiscard]]' if available // BSLA_NODISCARD_CPP17_IS_ACTIVE: 1 if 'BSLA_NODISCARD_CPP17' is active, or 0 // //@SEE_ALSO: bsla_annotations // //@DESCRIPTION: This component provides two preprocessor macros that annotate // entities that should not be ignored. Widely supported is annotating a // function such that a compiler warning will be generated if the return value // of the function is ignored, which is what 'BSLA_NODISCARD' does. C++17 and // later compilers also support annotating types, 'BSLA_NODISCARD_CPP17' is // defined when such annotation is supported. // ///Macro Reference ///--------------- //: 'BSLA_NODISCARD' //: This annotation causes a warning to be emitted if the caller of a //: so-annotated function does not use its return value. This is useful //: for functions where not checking the result is either a security //: problem or always a bug, such as with the 'realloc' function. //: The annotation cannot be used portably on constructors if C++03 support //: is required because the GNU compiler reuses their C (language) //: implementation for compatibility and that does not understand that //: constructors. Attempt to apply 'BSLA_NODISCARD' to a constructor will //: result in a warning from gcc: //: warning: 'warn_unused_result' attribute ignored [-Wattributes] //: 4 | BSLA_NODISCARD Type() {} //: | ^ // //: 'BSLA_NODISCARD_IS_ACTIVE' //: The macro 'BSLA_NODISCARD_IS_ACTIVE' is defined to 0 if //: 'BSLA_NODISCARD' expands to nothing and 1 otherwise. //: //: 'BSLA_NODISCARD_CPP17' //: This annotation can be used on both types and functions. Due to //: differences in compiler parser implementations this macro must be //: placed *after* the 'class' (or 'struct') keyword and before the name of //: the type; otherwise it might not compile. //: //: GNU gcc-9 does not fully implement this C++17 standard attribute as it //: is ignored on constructors and warns: //: warning: 'nodiscard' attribute applied to 'Type::Type()' with void //: return type [-Wattributes] //: 4 | BSLA_NODISCARD Type() {} //: | ^ ~~~ //: There is no known use case for marking individual constructors //: '[[ nodiscard ]]' instead of the whole type, so it is easy to avoid //: portability issues by marking the type itself nodiscard. //: //: Marking a type with 'BSLA_NODISCARD_CPP17' in effect makes any function //: (including constructors) that return such a type by value or create an //: object of that type (in case of constructors) behave as if they were //: all (individually) marked by 'BSLA_NODISCARD_CPP17'. This ability is //: very useful for guards or any other RAII types where using the object //: as part of a discarded-value expression has completely different //: behavior than using a (named) variable of it. // //: 'BSLA_NODISCARD_CPP17_IS_ACTIVE' //: The macro 'BSLA_NODISCARD_CPP17_IS_ACTIVE' is defined to 0 if //: 'BSLA_NODISCARD_CPP17' expands to nothing and 1 otherwise. // ///Usage Examples ///-------------- // This section illustrates intended use of this component. // ///Example 1: Square Root Function ///- - - - - - - - - - - - - - - - // First, we define a function, 'newtonsSqrt', which uses Newton's method for // calculating a square root. Since the function has no side effects, it // doesn't make sense to call it and ignore its result, so we annotate it with // 'BSLA_NODISCARD': //.. // BSLA_NODISCARD // double newtonsSqrt(double x); // double newtonsSqrt(double x) // // Take the square root of the specified 'x'. The behavior is // // undefined unless 'x' is positive. // { // BSLS_ASSERT(x > 0); // // double guess = 1.0; // for (int ii = 0; ii < 100; ++ii) { // guess = (guess + x / guess) / 2; // } // // return guess; // } //.. // Then, in 'main', we call it normally a few times and observe that it works // with no compiler warnings generated: //.. // printf("Square root of 9.0 = %g\n", newtonsSqrt(9.0)); // printf("Square root of 0.01 = %g\n", newtonsSqrt(0.01)); // printf("Square root of 0.917 * 0.917 = %g\n", newtonsSqrt(0.917 * 0.917)); //.. // Next, we call it and do nothing with the result, which will generate a // warning: //.. // newtonsSqrt(36.0); //.. // Now, we call it and explicitly void the result, which, with gcc, still won't // suppress the "unused result" warning: //.. // (void) newtonsSqrt(25.0); //.. // Finally, we observe the compiler warnings from the last 2 calls: //.. // .../bsla_nodiscard.t.cpp:289:22: warning: ignoring return value of 'double // newtonsSqrt(double)', declared with attribute warn_unused_result // [-Wunused-result] // newtonsSqrt(36.0); // ^ // .../bsla_nodiscard.t.cpp:294:29: warning: ignoring return value of 'double // newtonsSqrt(double)', declared with attribute warn_unused_result // [-Wunused-result] // (void) newtonsSqrt(25.0); // ^ //.. // ///Example 2: No-discard Type /// - - - - - - - - - - - - - // Suppose we create a guard type that is capable of closing delimiters that we // have opened while operating on an output stream. The example uses C I/O and // string literals for brevity. // // First, we define a guard type 'DelimiterGuard0': //.. // class DelimGuard0 { // private: // // DATA // const char *d_closingText_p; // Held, not owned // // public: // // CREATORS // explicit DelimGuard0(const char *closingText); // // Create a delimiter guard that upon its destruction prints the // // specified 'closingText' to 'stdout'. The behavior is undefined // // unless 'closingText' outlives the created object. // // ~DelimGuard0(); // // Print the closing text to the output file, then destroy this // // object. // }; //.. // Then we can write code that uses the guard properly: //.. // void guard0ProperUse() // { // printf("\n```ruby\n"); DelimGuard0 closeCode("```\n"); // // // Suppose long and complicated code with early returns writing some // // source code between the delimiters. Instead we write something // // trivial for brevity: // printf("puts 'Hello World'\n"); // } //.. // Next, we demonstrate that the guard works as intended: //.. // guard0ProperUse(); // prints: [\n]```ruby[\n]puts 'Hello World'[\n]```[\n] //.. // Then, we write code missing the variable name for the guard. By not giving // a variable name we turn what should be an automatic (local) variable // definition into a so-called expression statement: '<expression>;'. // Expression statements execute an expression for its side effects, then // destroy all temporaries created in the expression "at the semicolon". All // the 'printf' function calls below are expression statements, they just don't // have any temporaries to destroy. //.. // void discardedGuard0() // { // printf("("); DelimGuard0(")\n"); // printf("in-parens"); // } //.. // Next, we demonstrate the bug cause by the guard variable name missing: //.. // discardedGuard0(); // prints: ()[\n]in-parens //.. // Then, we add the no-discard annotation to our guard type directly: //.. // class BSLA_NODISCARD_CPP17 DelimGuardCpp17 { //.. // The rest is omitted for brevity. // // Next, we can write the buggy code again using the annotated type: //.. // void discardedGuardCpp17() // { // printf("["); DelimGuardCpp17("]"); // printf("in-brackets"); // } //.. // Finally, we can demonstrate using a C++17 compiler that we get a warning for // the buggy code: //.. // .../bsla_nodiscard.t.cpp:LLL:CC: warning: ignoring temporary created by a // constructor declared with 'nodiscard' attribute [-Wunused-value] // printf("["); DelimGuardCpp17("]"); // ^~~~~~~~~~~~~~~~~~~~ // // ...\bsla_nodiscard.t.cpp(227,36): warning C4834: discarding return value of // function with 'nodiscard' attribute //.. #include <bsls_compilerfeatures.h> #include <bsls_platform.h> #if defined(BSLS_COMPILERFEATURES_SUPPORT_ATTRIBUTE_NODISCARD) #define BSLA_NODISCARD [[ nodiscard ]] #define BSLA_NODISCARD_IS_ACTIVE 1 #define BSLA_NODISCARD_CPP17 BSLA_NODISCARD #define BSLA_NODISCARD_CPP17_IS_ACTIVE 1 #elif defined(BSLS_PLATFORM_CMP_GNU) // [[nodiscard]] not supported #if BSLS_COMPILERFEATURES_CPLUSPLUS >= 201103L #define BSLA_NODISCARD __attribute__((warn_unused_result)) #define BSLA_NODISCARD_IS_ACTIVE 1 #else // gcc attribute cannot be disabled using (void) in C++03 mode, so it // would create too many false warnings. #define BSLA_NODISCARD #define BSLA_NODISCARD_IS_ACTIVE 0 #endif #define BSLA_NODISCARD_CPP17 #define BSLA_NODISCARD_CPP17_IS_ACTIVE 0 #elif defined(BSLS_PLATFORM_CMP_CLANG) // nodiscard not supported && not g++ #define BSLA_NODISCARD __attribute__((warn_unused_result)) #define BSLA_NODISCARD_IS_ACTIVE 1 #define BSLA_NODISCARD_CPP17 __attribute__((warn_unused_result)) #define BSLA_NODISCARD_CPP17_IS_ACTIVE 1 #else // not (gcc/g++ or clang) && [[nodiscard]] not supported #define BSLA_NODISCARD #define BSLA_NODISCARD_IS_ACTIVE 0 #define BSLA_NODISCARD_CPP17 #define BSLA_NODISCARD_CPP17_IS_ACTIVE 0 #endif // not (gcc/g++ or clang) && [[nodiscard]] not supported #endif // ---------------------------------------------------------------------------- // Copyright 2019 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------