3

I stumbled upon this code in the SerenityOS project:

template<typename... Parameters>
void dbgln(CheckedFormatString<Parameters...>&& fmtstr, const Parameters&... parameters)

They are re-implementing an equivalent of println! from rust. An easier version of printf where you don't need to care about the argument type and is used this way:

dbgln("This is a {}", "test");

They are doing some compile time checks on the fmtstr making sure that there are no unclosed braces and that the number of braces match the number of arguments. That's why the FormatString struct need to be templated to have access to the number of argument at compile time.

But there is something I don't understand. I wrote an MWE with the code below which, in essence, reproduce what they are doing:

#include <stddef.h>

template<typename... Args>
void compiletime_fail(Args...);

template<typename ...Parameters>
struct UnconstrainedFormatString {
template <size_t size>
  consteval UnconstrainedFormatString(const char (&buffer)[size]): m_buffer(buffer), m_size(size) {
  }

  const char *m_buffer { nullptr };
  const size_t m_size { 0 };
};

template<typename T>
struct __IdentityType {
  using Type = T;
};

template<typename T>
using IdentityType = typename __IdentityType<T>::Type;

template<typename... Args>
using FormatString = UnconstrainedFormatString<IdentityType<Args>...>; // but why?

template<typename ...Parameters>
constexpr void println(FormatString<Parameters...>&& fmtstr, const Parameters& ...parameters) {
}

int main() {
  println("this is a test", 1, 2, 3);
}

if I used UnconstrainedFormatString in the println signature I get this error from the compiler:

/cplayground/code.cpp:32:3: error: no matching function for call to 'println'
  println("this is a test", 1, 2, 3);
  ^~~~~~~
/cplayground/code.cpp:28:16: note: candidate template ignored: could not match 'UnconstrainedFormatString<type-parameter-0-0...>' against 'char const[15]'
constexpr void println(UnconstrainedFormatString<Parameters...>&& fmtstr, const Parameters& ...parameters) {

In order for it to compile, I need to do that funky business with IdentityType.

Why do I need this?

6
  • Why do you need the UnconstrainedFormatString as a template? If you define it as a struct, everything will work fine with the UnconstrainedFormatString. Don't you overcomplicate things? Commented Aug 20, 2021 at 19:13
  • 1
    I think the purpose is to inspect at compile time that the number of braces matches the number of arguments. Commented Aug 20, 2021 at 19:16
  • 2
    To prevent deduction from that argument. See stackoverflow.com/questions/17433082/… Commented Aug 20, 2021 at 19:54
  • @ecatmur I think you might be onto something here. However in my example, there is no possible ambiguity because it is impossible to deduce the type of the variadic parameters from juste the format string. Care to elaborate a little more, maybe in an answer? Commented Aug 20, 2021 at 20:06
  • Ok so en.cppreference.com/w/cpp/language/… explains the IdentityType which is equivalent to std::type_identity. But its used is to avoid ambiguous type deduction which is not the case here. The error is different. Still need to understand where the error comes from to have a full picture. Commented Aug 21, 2021 at 6:06

1 Answer 1

1

See the https://en.cppreference.com/w/cpp/language/template_argument_deduction#Implicit_conversions :

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

How does the FormatString/IdentityType change things? The IdentityType suppresses the deduction of template parameters for const char[N] and now the string is used as a constructor parameter for UnconstrainedFormatString<...>.

You can learn more details here: https://humanreadablemag.com/issues/0/articles/how-to-avoid-template-type-deduction-in-c

When you use UnconstrainedFormatString only, the compiler tries to deduct UnconstrainedFormatString from const char [N] and fails since for templates it "doesn't know" that the conversion from const char[N] to UnconstrainedFormatString exists.

You can easily check with the

println(UnconstrainedFormatString<int, int, int>("test"), 1, 2, 3);

Since no conversion is needed here, it works, as well.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.