Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Can I check if an expression is type-dependent?

Something tells me the answer is "no", but I figured I’d ask anyway.

I want to check in a macro whether a specific expression is type-dependent. Here’s a motivating example:

#include <concepts>
#include <format>
#include <iostream>

template <typename T>
void Print(const T &value)
{
    if constexpr (std::default_initializable<std::formatter<T>>)
        std::cout << std::format("{}", value) << '\n';
    else
        std::cout << "??\n";
}

#define PRINT(...) Print(__VA_ARGS__)

// ---

struct Unknown {};

template <typename T>
void MaybePrint(const T &value)
{
    PRINT(value);
}

int main()
{
    PRINT(42); // "42"
    PRINT(Unknown{}); // "??", but I want a build error, because the expression is not type-dependent.
    MaybePrint(42); // "42"
    MaybePrint(Unknown{}); // "??", this is ok, since the expression is type-dependent.
}

I have a macro that prints any object passed to it (this is for a unit test framework).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

If it’s not printable, I obviously want a compilation error. But for type-dependent expressions I instead want to print a placeholder (to avoid forcing the user to write if constexpr (printable<T>) ... every time).

>Solution :

Moving the body of Print directly into the macro and tweaking the constraint gives the desired behavior:

#define PRINT(...)                                                     \
  {                                                                    \
    if constexpr (requires {                                           \
                    std::formatter<                                    \
                        std::remove_cvref_t<decltype(__VA_ARGS__)>>{}; \
                  })                                                   \
      std::cout << std::format("{}", __VA_ARGS__) << '\n';             \
    else                                                               \
      std::cout << "??\n";                                             \
  }

struct Unknown {};

template <typename T>
void MaybePrint(const T &value) {
  PRINT(value);
}

https://godbolt.org/z/4Po3xh9oP

I removed -stdlib=libc++ from Clang because it appears to have lack of library support for this example, but the underlying concept about hard errors vs. substitution failures works the same way in Clang as in GCC and MSVC.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading