Repository: conan-io/conan
Pull request: Adding C standard detection
Changelog: Feature: Auto detection of C standard.
Docs: https://github.com/conan-io/docs/pull/4097

- [x] Close https://github.com/conan-io/conan/issues/18281
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop2/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [ ] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one.


Problem statement:
[feature] Add C standard version into `detect_api`
### What is your suggestion?

Since GCC 15 switched the default C standard to C23, some of our recipes have broken. It would be helpful to have the ability to detect the C standard via detect_api, as certain patches need to be applied conditionally.

### Have you read the CONTRIBUTING guide?

- [x] I've read the CONTRIBUTING guide

Hints from issue discussion:
Hi @shtanko-sv 

Thanks for the suggestion.

The ``detect_api`` doesn't really have a "detect" c++std capability, but a ``default_cppstd(compiler, compiler_version)`` one, which means it is basically a hardcoded mapping of compiler versions.

I guess you would like a similar ``detect.default_cstd()`` method? It sounds doable, and relatively simple, in case you want to give it a try and contribute it yourself.

In the meantime, I think you can easily introduce a jinja conditional in your profiles for gcc 15, and define ``compiler.cstd``.
Also note that it this moment we are not planning to add ``compiler.cstd`` to the default detected profile, as that could be a bit more breaking than what we would like, so the plan is to keep it as an opt-in. 
Hi, sorry for late reply.

> I guess you would like a similar detect.default_cstd() method?

Yes, exactly that. I will take a look at the source code and will provide a PR.

> I think you can easily introduce a jinja conditional in your profiles for gcc 15, and define compiler.cstd

Yes, we did it for our profiles, as it's a really trivial change, but I thought that it could be useful for others to have it in "detect".

Thanks

Commit message: Remove cstd from default generated profiles

Co-authored-by: Abril Rincón Blanco <5364255+AbrilRBS@users.noreply.github.com>

Patch to review:
diff --git a/conan/internal/api/detect/detect_api.py b/conan/internal/api/detect/detect_api.py
index 6b7cbfb58b8..8611b560e61 100644
--- a/conan/internal/api/detect/detect_api.py
+++ b/conan/internal/api/detect/detect_api.py
@@ -347,6 +347,54 @@ def detect_cppstd(compiler, compiler_version):
     return cppstd
 
 
+def default_cstd(compiler, compiler_version):
+    """returns the default cstd for the compiler-version. This is not detected, just the default"""
+
+    def _clang_cstd_default(version):
+        if version >= "11":
+            return "gnu17"  # https://releases.llvm.org/11.0.0/tools/clang/docs/ReleaseNotes.html#c-language-changes-in-clang
+        elif version >= "4":  # 3.5 actually
+            return "gnu11"
+        else:
+            return "gnu99"  # It was gnu89 actually
+
+    def _gcc_cstd_default(version):
+        if version >= "15":  # https://www.gnu.org/software/gcc/gcc-15/changes.html#c
+            return "gnu23"
+        elif version >= "8":
+            return "gnu17"  # https://www.gnu.org/software/gcc/gcc-8/changes.html#c
+        elif version >= "5":
+            return "gnu11"  # https://www.gnu.org/software/gcc/gcc-5/changes.html#c
+        else:
+            return "gnu99"  # It was gnu89 actually
+
+    def _visual_cstd_default(version):
+        return None
+
+    def _apple_clang_cstd_default(version):
+        return None
+
+    def _intel_cstd_default(version):
+        return None
+
+    def _mcst_lcc_cstd_default(version):
+        return None
+
+    default = {
+        "gcc": _gcc_cstd_default(compiler_version),
+        "clang": _clang_cstd_default(compiler_version),
+        "apple-clang": _apple_clang_cstd_default(compiler_version),
+        "intel-cc": _intel_cstd_default(compiler_version),
+        "msvc": _visual_cstd_default(compiler_version),
+        "mcst-lcc": _mcst_lcc_cstd_default(compiler_version),
+    }.get(str(compiler), None)
+    return default
+
+
+def detect_cstd(compiler, compiler_version):
+    return default_cstd(compiler, compiler_version)
+
+
 def detect_default_compiler():
     """
         find the default compiler on the build machine
diff --git a/conan/internal/api/profile/detect.py b/conan/internal/api/profile/detect.py
index 7c2d2685d6e..7d714ec9018 100644
--- a/conan/internal/api/profile/detect.py
+++ b/conan/internal/api/profile/detect.py
@@ -1,6 +1,6 @@
 from conan.api.output import ConanOutput
 from conan.internal.api.detect.detect_api import detect_os, detect_arch, default_msvc_runtime, \
-    detect_libcxx, detect_cppstd, detect_default_compiler, default_compiler_version
+    detect_libcxx, detect_cppstd, detect_default_compiler, default_compiler_version, detect_cstd
 
 
 def detect_defaults_settings():


Code review comments:
diff_hunk: @@ -347,6 +347,54 @@ def detect_cppstd(compiler, compiler_version):
     return cppstd
 
 
+def default_cstd(compiler, compiler_version):
+    """returns the default cstd for the compiler-version. This is not detected, just the default"""
+
+    def _clang_cstd_default(version):
+        if version >= "11":
+            return "gnu17"  # https://releases.llvm.org/11.0.0/tools/clang/docs/ReleaseNotes.html#c-language-changes-in-clang
+        elif version >= "4":  # 3.5 actually
+            return "gnu11"
+        else:
+            return "gnu99"  # It was gnu89 actually
+
+    def _gcc_cstd_default(version):
+        if version >= "15":  # https://www.gnu.org/software/gcc/gcc-15/changes.html#c
+            return "gnu23"
+        elif version >= "8":
+            return "gnu17"  # https://www.gnu.org/software/gcc/gcc-8/changes.html#c
+        elif version >= "5":
+            return "gnu11"  # https://www.gnu.org/software/gcc/gcc-5/changes.html#c
+        else:
+            return "gnu99"  # It was gnu89 actually
+
+    def _visual_cstd_default(version):
+        return None
+
+    def _apple_clang_cstd_default(version):
+        return None
+
+    def _intel_cstd_default(version):
+        return None
+
+    def _mcst_lcc_cstd_default(version):
+        return None
+
+    default = {
+        "gcc": _gcc_cstd_default(compiler_version),
+        "clang": _clang_cstd_default(compiler_version),
+        "apple-clang": _apple_clang_cstd_default(compiler_version),
+        "intel-cc": _intel_cstd_default(compiler_version),
+        "msvc": _visual_cstd_default(compiler_version),
+        "mcst-lcc": _mcst_lcc_cstd_default(compiler_version),
+    }.get(str(compiler), None)
+    return default
+
+
+def detect_cstd(compiler, compiler_version):
path: conan/internal/api/detect/detect_api.py
text: @user1:
This kind of "alias" wouldn't be necessary, unless Conan ``conan profile detect`` would use a different ``cstd`` than the compiler default in the detection, but as ``conan profile detect`` won't add the ``cstd`` at this moment, this might not be necessary

@author:
I can remove it, but it might be better to keep it if you decide to add this to conan profile detect in the future. It's up to you.

@user1:
We typically try to follow a "minimalistic" approach, only add what is necessary at the moment, and if in the future the ``detect`` is needed, we can safely add it without breaking.
The other way round can be more problematic, being them equivalent at this moment, some users might mistake the ``detect_cstd`` to retrieve the compiler cstd, not the Conan default-defined, and if we change that behavior in the future, they could consider it breaking, even if it was just their misuse of the function. What is not there cannot be misused.

@author:
Got it, I will remove