scons icon indicating copy to clipboard operation
scons copied to clipboard

C/C++ scanner: add __has_include

Open mwichmann opened this issue 1 year ago • 3 comments

A preprocessor identifier __has_include was added to the C standard in the 2023 edition; the same identifier has appeared in the C++ standard since the 2017 edition (one of the justifications for adding to C was to keep the preprocessors relatively aligned). The identifier allows coding conditional inclusion of a header file, and thus affects SCons dependency scanning. Since most C compilers are also C++ compilers, this identifier is widely implemented already.

From the standard, here is example usage:

#if __has_include(<optional.h>)
  #include <optional.h>
  #define have_optional 1
#elif __has_include(<experimental/optional.h>)
  #include <experimental/optional.h>
  #define have_optional 1
  #define have_experimental_optional 1
#endif
#ifndef have_optional
  #define have_optional 0
#endif

As a shorter write-up than going through the entire standard, here is the accepted proposal which added it to C:

https://open-std.org/JTC1/SC22/WG14/www/docs/n2799.pdf

mwichmann avatar Apr 20 '24 13:04 mwichmann

For reading enjoyment, here's a snazzy C++ example from cppreference.com using __has_include:

#if __has_include(<optional>)
#  include <optional>
#  define has_optional 1
   template<class T> using optional_t = std::optional
#elif __has_include(<experimental/optional>)
#  include <experimental/optional>
#  define has_optional -1
   template<class T> using optional_t = std::experimental::optional
#else
#  define has_optional 0
#  include <utility>
 
template<class V>
class optional_t
{
    V v_{}; bool has_{false};
public:
    optional_t() = default;
    optional_t(V&& v) : v_(v), has_{true} {}
    V value_or(V&& alt) const& { return has_ ? v_ : alt; }
    /*...*/
};
#endif

Don't have any clear idea how to teach the scanner to handle this case, where a system header is used if it exists, if not an experimental header is used, and if neither, some inline code is used. We don't usually generate dependencies for "system headers", but the algorithm is unsophisticated - if it's not found, assume it's a system header and don't generate the dep, which has the potential to miss headers which will be generated by the build. There's of course no rule that says this check-for-include-file functionality has to be limited to system headers.

mwichmann avatar Apr 21 '24 16:04 mwichmann

Does the default scanner do ifdefs? or is it just the optional scanner which does?

bdbaddog avatar Apr 22 '24 01:04 bdbaddog

They use the same base class which does recognize ifdef and friends, but doesn't evaluate them; the conditional scanner is "smarter" and adds extra stuff.

mwichmann avatar Apr 22 '24 14:04 mwichmann