Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C/C++ scanner: add __has_include #4517

Open
mwichmann opened this issue Apr 20, 2024 · 3 comments
Open

C/C++ scanner: add __has_include #4517

mwichmann opened this issue Apr 20, 2024 · 3 comments
Labels
C23 Features new in the 2023 C Standard enhancement scanner

Comments

@mwichmann
Copy link
Collaborator

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 mwichmann added the C23 Features new in the 2023 C Standard label Apr 21, 2024
@mwichmann
Copy link
Collaborator Author

mwichmann commented Apr 21, 2024

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.

@bdbaddog
Copy link
Contributor

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

@mwichmann
Copy link
Collaborator Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C23 Features new in the 2023 C Standard enhancement scanner
Projects
None yet
Development

No branches or pull requests

2 participants